Unit testing file upload in a controller with Java Play Framework 2.3.x
Asked Answered
B

1

6

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/

Bentz answered 25/9, 2015 at 22:30 Comment(6)
Hi, I'm the author of the article. I tested this with Play 2.2 but it seems that few things changed. I will test it with newer version, but meanwhile you can check example in scala github.com/playframework/playframework/blob/…Houphouetboigny
As you are using Java maybe it will help to look at this: github.com/playframework/playframework/blob/…Frontispiece
Thanks for the reply @FrEaKmAn, I'll try to checkout that Scala version when I get a free moment, thanks.Bentz
@Anton, unfortunately that example is from the perspective of the receiver (Controller), and I was trying to automate from the client's perspective, or the user of the file upload.Bentz
@Bentz Did you find a solution? I'm facing the same problem.Ching
I haven't @Kris, I meant to play with it more, but haven't been able to find the time.Bentz
C
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"));

Cephalochordate answered 1/12, 2016 at 12:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.