Search code examples
javaunit-testingplayframeworkjunit4playframework-2.5

How to create a Form<T> for a play (java) unit test now that Form.form() is deprecated?


In application code when dealing with forms it is recommended to use a FormFactory to create a Form wrapper around the form of type T. But when it comes to testing, what is the way to create a Form? (Do you have to inject FormFactory in the test?)

My app does something similar to that:

class MyAmazingClass {
    private final FormFactory formFactory;

    @Inject
    MyAmazingClass(FormFactory formFactory) {
        this.formFactory = formFactory;
    }

    public CompletionStage<Result> myAmazingMethodHandlingForms() {
        Form<String> form = formFactory.form(String.class).bindFromRequest();
        // ... Actually doing something
        return null;
    }
}

What shall my test class (for unit testing) looks like?

I am trying something like this but I think I should not try to inject the FormFactory (also it does not seems to work):

public class MyAmazingClassTest extends WithApplication {

    @Mock
    FormFactory mockedFormFactory;

    @Inject
    FormFactory realFormFactory;

    MyAmazingClass myAmazingClass;

    @Override
    protected Application provideApplication() {
        return new GuiceApplicationBuilder().build();
    }

    @Before
    public void setUp() throws Exception {
        myAmazingClass = new MyAmazingClass(mockedFormFactory);
    }

    @Test
    public void testMyAmazingMethodHandlingForms() throws Exception {
        String myString = "ciao";
        Form<String> stringForm = realFormFactory.form(String.class).fill(myString);
        when(mockedFormFactory.form(eq(String.class)).bindFromRequest()).thenReturn(stringForm);
        myAmazingClass.myAmazingMethodHandlingForms();
        // Some assertions...
    }
}

I am using JUnit 4, Java 8 and Play framework 2.5.


Solution

  • I would say that mixing mocks with the real application is not the best idea here. You should either use mocks (and avoid WithApplication), or you can use the "real" instances by calling app.injector().instanceOf() (including for your MyAmazingClass). For example, when only using mocks:

    public class MyAmazingClassTest {
    
        @Test
        public void testMyAmazingMethodHandlingForms() throws Exception {
            Form<String> form = mock(Form.class);
            // setup the mocked form as you expect it to behave
    
            FormFactory formFactory = mock(FormFactory.class);
            when(formFactory.form(eq(String.class)).bindFromRequest()).thenReturn(form);
    
            MyAmazingClass myAmazingClass = new MyAmazingClass(formFactory);
            myAmazingClass.myAmazingMethodHandlingForms();
    
            // Some assertions...
        }
    }
    

    Testing using the real instances would requires you to do a request, since apparently, you are binding from the request:

    public class MyAmazingClassTest extends WithApplication {
    
        @Test
        public void testMyAmazingMethodHandlingForms() throws Exception {
            Map<String, String> formData = new HashMap<>();
            formData.put("some", "value");
            // fill the form with the test data
            Http.RequestBuilder fakeRequest = Helpers.fakeRequest().bodyForm(formData).method(Helpers.POST);
    
            Result result = Helpers.route(app, fakeRequest);
    
            // make assertions over the result or something else.
        }
    }