Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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/

like image 483
jcreason Avatar asked Sep 25 '15 22:09

jcreason


People also ask

How do I upload a file to play framework?

The standard way to upload files in a web application is to use a form with a special multipart/form-data encoding, which lets you mix standard form data with file attachment data. Note: The HTTP method used to submit the form must be POST (not GET ). The getRef() method gives you a reference to a TemporaryFile .

How do you write unit test cases for file upload?

Ways to write unit test code for File upload on Controllers: For file upload related testing we need to Mock HTTPContext, HTTPContext. Server and HttpPostedFileBase. These classes handle the file uploading and saving.


1 Answers

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"));

like image 112
Andriy Avatar answered Oct 25 '22 16:10

Andriy