如何在Play 2.2.2中测试文件上传?

时间:2014-06-06 16:44:56

标签: scala playframework playframework-2.2

我使用的是Play 2.2.2并且尝试将测试添加到我的文件上传功能中失败了。我知道有很多关于这方面的问题,我已经检查了很多,但是要么他们不工作,要么API已被弃用且不起作用,或者失败并出现一些虚假错误。

这是浏览器中有效请求的样子(使用Firefox的WebDeveloper插件):

-----------------------------479326525221683770414613115
Content-Disposition: form-data; name="files[]"; filename="upload_aum_sample.csv"
Content-Type: text/csv

AccountName,AuM,Name
IX_CH1,10,A
IX_CH2,20,B
IX_CH3,30,C
IX_CH4,40,D
IX_CH5,50,E
IX_CH6,60,F
IX_CH7,70,G
IX_CH8,80,H

-----------------------------479326525221683770414613115--

这是我迄今为止没有成功的尝试:


编译并发出警告,即routeAndCall已被弃用:

"upload file correctly" in new WithApplication {
  val fileName = "upload_aum_sample.csv"
  val file = getClass().getResource(fileName).getFile()
  val data = MultipartFormData(Map(), List(FilePart("files[]", fileName, Some("text/csv"), file)), List(), List())
  val result = routeAndCall(FakeRequest(POST, "/aum/upload/do", FakeHeaders(), data).withSession("username" -> "Test")).get    
  status(result) must equalTo(OK)
  contentType(result) must beSome.which(_ == "application/json")
}

并导致异常:

[info] ! upload file correctly
[error]   MatchError: <function1> (of class play.core.Router$Routes$$anon$4) (Helpers.scala:187)
[error] play.api.test.RouteInvokers$$anonfun$routeAndCall$1.apply(Helpers.scala:187)
[error] play.api.test.RouteInvokers$$anonfun$routeAndCall$1.apply(Helpers.scala:187)
[error] play.api.test.RouteInvokers$class.routeAndCall(Helpers.scala:187)
[error] AumUploadPageSpec.routeAndCall(AumUploadPageSpec.scala:30)
[error] play.api.test.RouteInvokers$class.routeAndCall(Helpers.scala:178)
[error] AumUploadPageSpec.routeAndCall(AumUploadPageSpec.scala:30)
[error] AumUploadPageSpec$$anonfun$12$$anon$3$delayedInit$body.apply(AumUploadPageSpec.scala:73)
[error] play.api.test.WithApplication$$anonfun$around$1.apply(Specs.scala:20)
[error] play.api.test.WithApplication$$anonfun$around$1.apply(Specs.scala:20)
[error] play.api.test.PlayRunners$class.running(Helpers.scala:45)
[error] play.api.test.Helpers$.running(Helpers.scala:364)
[error] play.api.test.WithApplication.around(Specs.scala:20)
[error] play.api.test.WithApplication.delayedInit(Specs.scala:17)
[error] AumUploadPageSpec$$anonfun$12$$anon$3.<init>(AumUploadPageSpec.scala:48)
[error] AumUploadPageSpec$$anonfun$12.apply(AumUploadPageSpec.scala:48)
[error] AumUploadPageSpec$$anonfun$12.apply(AumUploadPageSpec.scala:48)

编译罚款无警告

"upload file correctly" in new WithApplication {
  val fileName = "upload_aum_sample.csv"
  val file = getClass().getResource(fileName).getFile()
  val data = MultipartFormData(Map(), List(FilePart("files[]", fileName, Some("text/csv"), file)), List(), List())
  val result = controllers.Application.uploadDo("aum")(FakeRequest(POST, "/aum/upload/do", FakeHeaders(), data).withSession("username" -> "Test")).run
  status(result) must equalTo(OK) // <<<<<<< test fails here 
  contentType(result) must beSome.which(_ == "application/json")
}

但是测试失败是由于服务器响应400而不是200而不是OK

[info] x upload file correctly
[error]  '400' is not equal to '200' (AumUploadPageSpec.scala:53)

更新1:如果嵌入文件内容而不是文件,我仍会得到相同的错误,即 改变

val file = getClass().getResource(fileName).getFile()

val file = scala.io.Source.fromFile(getClass().getResource(fileName).getFile()).map(_.toByte).toArray

更新2:分别是路由和服务器端代码:

# Generic controllers
POST  /:context/upload/do  controllers.Application.uploadDo(context: String)

//------------------------------------------------------------------------
/**
 * Action that uploads a file for a given context
 * @param context the input context
 */
def uploadDo(context: String) = Action(parse.multipartFormData) { implicit request ⇒
 request.body.file("files[]").map { file ⇒
   val filename = file.filename
   val contentType = file.contentType
 }
 Ok(Json.parse(
   """{"files": [
    {
      "name": "picture1.jpg",
      "size": 902604,
      "error": "Filetype not allowed"
    },
    {
      "name": "picture2.jpg",
      "size": 841946,
      "error": "Filetype not allowed"
    }
]}"""))
}

1 个答案:

答案 0 :(得分:1)

this question的最佳答案(令人恼火地未被接受)解决了我的问题。为了完整起见,我将其包括在内。

trait FakeMultipartUpload {
  case class WrappedFakeRequest[A](fr: FakeRequest[A]) {
    def withMultipart(parts: (String, ContentBody)*) = {
      // create a multipart form
      val entity = new MultipartEntity()
      parts.foreach { part =>
        entity.addPart(part._1, part._2)
      }

      // serialize the form
      val outputStream = new ByteArrayOutputStream
      entity.writeTo(outputStream)
      val bytes = outputStream.toByteArray

      // inject the form into our request
      val headerContentType = entity.getContentType.getValue
      fr.withBody(bytes).withHeaders(CONTENT_TYPE -> headerContentType)
    }

    def withFileUpload(fileParam: String, file: File, contentType: String) = {
      withMultipart(fileParam -> new FileBody(file, contentType))
    }
  }

  implicit def toWrappedFakeRequest[A](fr: FakeRequest[A]) = WrappedFakeRequest(fr)

  // override Play's equivalent Writeable so that the content-type header from the FakeRequest is used instead of application/octet-stream  
  implicit val wBytes: Writeable[Array[Byte]] = Writeable(identity, None)
}

@RunWith(classOf[JUnitRunner])
class AumUploadPageSpec extends PlaySpecification with FakeMultipartUpload {
  //------------------------------------------------------------------------
  "upload file correctly" in new WithApplication {
    val fileName = "idxsrs_aum_2014-06-04.csv"
    val uploadFile = new File(getClass().getResource(fileName).getPath())
    val request = FakeRequest(POST, "/aum/upload/do").withFileUpload("files[]", uploadFile, "text/csv")
    val response = route(request).get
    status(response) must equalTo(OK)
    contentType(response) must beSome.which(_ == "application/json")
  }
}