Documentation

You are viewing the documentation for the 2.8.20 release in the 2.8.x series of releases. The latest stable release series is 3.0.x.

§Writing functional tests

Play provides a number of classes and convenience methods that assist with functional testing. Most of these can be found either in the play.test package or in the Helpers class.

You can add these methods and classes by importing the following:

import play.test.*;
import static play.test.Helpers.*;

§Creating Application instances for testing

Play frequently requires a running Application as context. To provide an environment for tests, Play provides helpers that produce new application instances for testing:

import static play.test.Helpers.*;
Application fakeApp = fakeApplication();

Application fakeAppWithMemoryDb = fakeApplication(inMemoryDatabase("test"));

§Injecting tests

If you’re using Guice for dependency injection then an Application for testing can be built directly. You can also inject any members of a test class that you might need. It’s generally best practice to inject members only in functional tests and to manually create instances in unit tests.

@Inject Application application;

@Before
public void setup() {
  Module testModule =
      new AbstractModule() {
        @Override
        public void configure() {
          // Install custom test binding here
        }
      };

  GuiceApplicationBuilder builder =
      new GuiceApplicationLoader()
          .builder(new Context(Environment.simple()))
          .overrides(testModule);
  Guice.createInjector(builder.applicationModule()).injectMembers(this);

  Helpers.start(application);
}

@After
public void teardown() {
  Helpers.stop(application);
}

§Testing with an application

To run tests with an Application, you can do the following:

@Test
public void findById() {
  running(
      fakeApplication(inMemoryDatabase("test")),
      () -> {
        Computer macintosh = Computer.findById(21l);
        assertEquals("Macintosh", macintosh.name);
        assertEquals("1984-01-24", formatted(macintosh.introduced));
      });
}

You can also extend WithApplication, this will automatically ensure that an application is started and stopped for each test method:

public class FunctionalTest extends WithApplication {

§Testing with a Guice application

To run tests with an Application created by Guice, you can do the following:

@Test
public void findById() {
  ClassLoader classLoader = classLoader();
  Application application =
      new GuiceApplicationBuilder()
          .in(new Environment(new File("path/to/app"), classLoader, Mode.TEST))
          .build();

  running(
      application,
      () -> {
        Computer macintosh = Computer.findById(21l);
        assertEquals("Macintosh", macintosh.name);
        assertEquals("1984-01-24", macintosh.introduced);
      });
}

Note that there are different ways to customize the Application creation when using Guice to test.

§Testing a Controller Action through Routing

With a running application, you can retrieve an action reference from the path for a route and invoke it. This also allows you to use RequestBuilder which creates a fake request:

import play.mvc.Http.RequestBuilder;
@Test
public void testBadRoute() {
  RequestBuilder request = Helpers.fakeRequest().method(GET).uri("/xx/Kiwi");

  Result result = route(app, request);
  assertEquals(NOT_FOUND, result.status());
}

It is also possible to create the RequestBuilder using the reverse router directly and avoid hard-coding the router path:

@Test
public void testGoodRouteCall() {
  RequestBuilder request = Helpers.fakeRequest(routes.HomeController.index());

  Result result = route(app, request);
   assertEquals(OK, result.status());
}

Note: the reverse router is not executing the action, but instead only providing a Call with information that will be used to create the RequestBuilder and later invoke the the action itself using Helpers.route(Application, RequestBuilder). That is why it is not necessary to pass a Http.Request when using the reverse router to create the Http.RequestBuilder in tests even if the action is receiving a Http.Request as a parameter.

§Testing with a server

Sometimes you want to test the real HTTP stack from within your test. You can do this by starting a test server:

@Test
public void testInServer() throws Exception {
  TestServer server = testServer(3333);
  running(
      server,
      () -> {
        try (WSClient ws = WSTestClient.newClient(3333)) {
          CompletionStage<WSResponse> completionStage = ws.url("/").get();
          WSResponse response = completionStage.toCompletableFuture().get();
          assertEquals(OK, response.getStatus());
        } catch (Exception e) {
          logger.error(e.getMessage(), e);
        }
      });
}

Just as there exists a WithApplication class, there is also a WithServer which you can extend to automatically start and stop a TestServer for your tests:

public class ServerFunctionalTest extends WithServer {

  @Test
  public void testInServer() throws Exception {
    OptionalInt optHttpsPort = testServer.getRunningHttpsPort();
    String url;
    int port;
    if (optHttpsPort.isPresent()) {
      port = optHttpsPort.getAsInt();
      url = "https://localhost:" + port;
    } else {
      port = testServer.getRunningHttpPort().getAsInt();
      url = "http://localhost:" + port;
    }
    try (WSClient ws = play.test.WSTestClient.newClient(port)) {
      CompletionStage<WSResponse> stage = ws.url(url).get();
      WSResponse response = stage.toCompletableFuture().get();
      assertEquals(NOT_FOUND, response.getStatus());
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

§Testing with a browser

If you want to test your application from with a Web browser, you can use Selenium WebDriver. Play will start the WebDriver for you, and wrap it in the convenient API provided by FluentLenium.

@Test
public void runInBrowser() {
  running(
      testServer(),
      HTMLUNIT,
      browser -> {
        browser.goTo("/");
        assertEquals("Welcome to Play!", browser.el("#title").text());
        browser.$("a").click();
        assertEquals("login", browser.url());
      });
}

And, of course there, is the WithBrowser class to automatically open and close a browser for each test:

public class BrowserFunctionalTest extends WithBrowser {

  @Test
  public void runInBrowser() {
    browser.goTo("/");
    assertNotNull(browser.el("title").text());
  }
}

Next: Testing with Guice