-->

Unit testing file upload in a controller with Java

2020-07-11 09:58发布

问题:

After working most of the day I feel like I am fairly close to a solution on how to test a controller method which accepts file uploads from JUnit. My juint test code is as follows:

Map<String, String> postData = makePostMap(uploadForm);
File file = new File("test/resources/shared/uploads/blank.csv");
TemporaryFile temporaryFile = new TemporaryFile(file);

MultipartFormData.FilePart filePath = new MultipartFormData.FilePart(
        "file",
        "file.csv",
        new scala.Some<>("text/csv"),
        temporaryFile);

List<MultipartFormData.FilePart> fileParts = Lists.newArrayList(filePath);
scala.collection.immutable.Seq files = JavaConversions.asScalaBuffer(fileParts).toList();

Map<String, scala.collection.immutable.Seq<String>> postData2 = new HashMap<>();
for (String s : postData.keySet()) {
    postData2.put(s, JavaConversions.asScalaBuffer(Lists.newArrayList(postData.get(s))).toList());
}
scala.collection.immutable.Map<String, scala.collection.immutable.Seq<String>> scalaMap =
        JavaConversions.mapAsScalaMap(postData2).toMap(Predef.<Tuple2<String, scala.collection.immutable.Seq<String>>>conforms());

MultipartFormData formData = new MultipartFormData(scalaMap, files, null, null);
AnyContentAsMultipartFormData body = new AnyContentAsMultipartFormData(formData);

// run
login(employee);
String url = routes.ManageContacts.uploadCsv().url();
FakeRequest fakeRequest = new FakeRequest(POST, url).withBody(body);
fakeRequest = getAuthenticatedRequest(fakeRequest, employee);

result = route(fakeRequest);

assertThat(status(result)).isEqualTo(OK)

However, I get an exception (below) when the FakeRequest is routed to.

[error] Test controllers.ManageContactsTest.testUploadCsv failed: scala.MatchError: AnyContentAsMultipartFormData(MultipartFormData(Map(clearExisting -> List(false), survey -> List(11), bosMode -> List(false)),List(FilePart(file,file.csv,Some(text/csv),TemporaryFile(test/resources/shared/uploads/blank.csv))),null,null)) (of class play.api.mvc.AnyContentAsMultipartFormData), took 0.255 sec
[error]     at play.api.test.RouteInvokers$class.jRoute(Helpers.scala:255)
[error]     at play.api.test.Helpers$.jRoute(Helpers.scala:403)
[error]     at play.api.test.Helpers.jRoute(Helpers.scala)
[error]     at play.test.Helpers.route(Helpers.java:445)
[error]     at play.test.Helpers.route(Helpers.java:437)
[error]     at play.test.Helpers.route(Helpers.java:433)
[error]     at controllers.ManageContactsTest.testUploadCsv(ManageContactsTest.java:121)
[error]     ...

Diving down into the stack trace, I find the following scala match statement in the file: /Users/jcreason/bin/playframework-2.3.8/framework/src/play-test/src/main/scala/play/api/test/Helpers.scala:253

  def jRoute[T](app: Application, r: FakeRequest[T]): Option[Future[Result]] = {
    (r.body: @unchecked) match {
      case body: AnyContentAsFormUrlEncoded => route(app, r, body)
      case body: AnyContentAsJson => route(app, r, body)
      case body: AnyContentAsXml => route(app, r, body)
      case body: AnyContentAsText => route(app, r, body)
      case body: AnyContentAsRaw => route(app, r, body)
      case body: AnyContentAsEmpty.type => route(app, r, body)
      //case _ => MatchError is thrown
    }
  }

Since I'm passing through AnyContentAsMultipartFormData, it throws this exception as it's not handled by the match. Does anyone know how to get around this? Or could point me in the direction of a different solution to this (aside from obvious answers just as selenium)?

For reference, I pulled some of this code from:

http://www.erol.si/2014/02/how-to-test-file-uploads-in-play-framework-java/

回答1:

This code may be useful:

import static play.test.Helpers.*;
import static org.junit.Assert.*;
import java.util.*;
import org.junit.Test;
import akka.stream.javadsl.Source;
import akka.util.ByteString;
import play.mvc.Http.MultipartFormData.*;
import play.mvc.Http.RequestBuilder;
import play.mvc.Result;
import play.test.*;

class UploadFileTest extends WithApplication {
    @Test
    public void uploadTest() {
        Part<Source<ByteString, ?>> part = new FilePart<>("key", "fileName", "application/octet-stream",
                Source.empty());
        List<Part<Source<ByteString, ?>>> data = Arrays.asList(part);

        RequestBuilder requestBuilder = fakeRequest(controllers.routes.UploadController.upload()).method(POST)
                .bodyMultipart(data, mat);

        Result result = route(app, requestBuilder);

        assertEquals(Helpers.OK, result.status());
    }
}

If you want to send not empty file,
instead of Source.empty() use FileIO.fromFile(new File("path/to/file"));