PlayFramework Testing: Uploading File in Fake Request Errors

ⅰ亾dé卋堺 提交于 2020-01-02 05:50:09

问题


I want to test my method, which requires uploading a file. It is initialized like this:

val tempFile = TemporaryFile(new java.io.File("/home/ophelia/Desktop/students"))
val part = FilePart[TemporaryFile](
  key = "students", 
  filename = "students", 
  contentType = Some("text/plain"), 
  ref = tempFile)
val files = Seq[FilePart[TemporaryFile]](part)
val formData = MultipartFormData(
  dataParts = Map(), 
  files = Seq(part), 
  badParts = Seq(), 
  missingFileParts = Seq())

I pass it into the FakeRequest:

val result = route(
  FakeRequest(POST, "/api/courses/"+"4f3c4ec9-46bf-4a05-a0b2-886c2040f2f6"+"/import" )
    .withHeaders("Authorization" -> ("Session " + testSessionA.id.string))
    .withMultipartFormDataBody(formData)
)

But when I run the test I get the following error:

Cannot write an instance of play.api.mvc.AnyContentAsMultipartFormData to HTTP response. Try to define a Writeable[play.api.mvc.AnyContentAsMultipartFormData]

What am I doing wrong and how to fix it? I looked on the internet, I didnt find any useful way to understand and resolve this problem.


回答1:


It's important to remember that http requests are entirely text. route() takes an implicit Writeable to convert the body type of the provided request into text. Without the right Writeable, there is no way to know how to turn MultipartFormData into a request body.

There doesn't seem to be a Writeable for MultipartFormData, but you can provide your own. jroper has a great Writeable you could use for reference. (EDIT: That code is buggy, here's a working Writeable for AnyContentAsMultipartFormData)

Once you have your Writeable, you will need to make it accessible to your call to route(). Bear in mind, you currently have a FakeRequest[AnyContentAsMultipartFormData], not a FakeRequest[MultipartFormData]. You can either convert your request first:

val request = FakeRequest(POST, 
    "/api/courses/"+"4f3c4ec9-46bf-4a05-a0b2-886c2040f2f6"+"/import" )
        .withHeaders("Authorization" -> ("Session "))
        .withMultipartFormDataBody(formData)
route(request.map(_.mdf).asInstanceOf[FakeRequest[MultipartFormData[TemporaryFile]]])

or make your Writeable a Writeable[AnyContentAsMultipartFormData].




回答2:


route for a given Request[T] requires an implicit parameter of type Writeable[T] that knows how to serialize the request body, because it will actually call the controller action just like an actual web request would, by pushing bytes onto it.

The problem is that there is no Writeable[MultipartFormData] predefined (you can see which are in play.api.test.Writeables).

This means you basically have two options:

  1. write your own Writeable that serializes a MultipartFormData into bytes
  2. Skip the routing part and call the action directly instead, like in the accepted answer in Play Framework Testing using MultipartFormData in a FakeRequest. This way of testing actions takes a shortcut and does not actually serialize and deserialize the request.

IMHO the first option is way too much pain for the gain, but if you go down that road, maybe contribute it to play when you succeed.




回答3:


One of the possible solutions is to use wsUrl. For example

"File uploading action" should {

  "upload sent file and result in ID" in {
    val file = Paths.get(getClass.getResource("/1.txt").toURI)
    val action = wsUrl("/upload").post(Source.single(FilePart("file", "hello.txt", Option("text/plain"), FileIO.fromPath(file))))

    val res = Await.result(action, timeout)

    res.status mustBe OK
    res.body contains "123"
 }
}


来源:https://stackoverflow.com/questions/29701517/playframework-testing-uploading-file-in-fake-request-errors

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!