8

The title pretty much says it all. I would like to set up a traditional JUnit test to mock a controller's dependencies and run tests against actions.

I've found that I can achieve my goal like this:

public class AccountsControllerTest {
    private controllers.Accounts accountsController;

    @Test
    public void test() {
        running(fakeApplication(), new Runnable() {
            public void run() {
                accountsController = new controllers.Accounts();
                accountsController.setAccountsWorkflow(mock(workflow.Accounts.class));
            }
        });
    }
}

The obvious problem here is that I'm instantiating my class under test and injecting mock dependencies from the test method itself, when I should be doing that in the setup() method. It seems that the setup() method is useless if I'm going to test my controller in a traditional way.

Of course I can test controllers the way Play recommends, but my application is dependent on an external SOAP web service, so I need unit tests to show that our code is working when their servers are down.

So, what's the best way to unit test a Play controller using mocks while still taking advantage of setup() and teardown() methods?

Edit

I realize I'm assuming some knowledge here, so for those who are unaware, controller instantiation in a unit test must be wrapped in a running() function or Play! will throw a runtime exception saying that no application has been started.

Samo
  • 8,202
  • 13
  • 58
  • 95
  • http://www.joergviola.de/blog/2012/06/04/page-driven-functional-tests-for-play-2-dot-0/ is a nice way for testing too, but still has the mock problem. You could try to run the test without fakeApplication. See http://stackoverflow.com/questions/10381354/how-to-manipulate-session-request-and-response-for-test-in-play2-0 – niels Jun 22 '12 at 11:38
  • http://stackoverflow.com/a/10114621/89509 – Blake Pettersson Jul 13 '12 at 14:47
  • Thanks for the example. How were you able to instantiate the controller outside of the `running` function? This constraint is what prevents me from leveraging JUnit's `setup` method so I don't have to setup mocks on every test method. Your example doesn't really demonstrate this. – Samo Jul 13 '12 at 16:40
  • @Samo Any luck with is? I want to be able to verify the view that is returned from the controller, but not by checking HTML contents. I want to verify that the right view name is being passed. – Ankit Dhingra Jul 30 '12 at 11:38
  • I don't know if this data is available for Play's `Result` type. Doesn't appear to be. You can use the format shown in my question to unit test your controller with mocks, you just won't be able to use your `setup()` method, so you'll be repeating a lot of mocking from test method to test method. – Samo Jul 30 '12 at 19:13

1 Answers1

1

You could accomplish this using Mockito and Play's FakeApplication and setting the static Http.Context variable.

This way you can write the test like all other JUnit test.

Example:

...
import static play.test.Helpers.status;
import play.test.FakeApplication;
import play.test.Helpers;
import play.mvc.Http;
import play.mvc.Result;
...

@RunWith(MockitoJUnitRunner.class)
public class ApplicationTest {

  public static FakeApplication app;

  @Mock
  private Http.Request request;

  @BeforeClass
  public static void startApp() {
      app = Helpers.fakeApplication();
      Helpers.start(app);

  }

  @Before
  public void setUp() throws Exception {
      Map<String, String> flashData = Collections.emptyMap();
      Http.Context context = new Http.Context(request, flashData, flashData);
      Http.Context.current.set(context);
  }

  @Test
  public void testIndex() {
      final Result result = Application.index();
      assertEquals(play.mvc.Http.Status.OK, status(result));
  }

  @AfterClass
  public static void stopApp() {
      Helpers.stop(app);
  }
Zapodot
  • 462
  • 5
  • 10
  • I didn't know you could set the current Http.Context like that -- that's cool; thanks. – duma Sep 20 '12 at 14:32
  • I found that by setting `Http.Context.current.set(context);` my subsequent tests relying on Application failed. So you must set the context back to its original state in an @AfterClass. – Richard Lewan Sep 03 '15 at 01:06