Documentation

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.api.test package or in the Helpers object.

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

import play.api.test._
import play.api.test.Helpers._

FakeApplication

Play frequently requires a running Application as context: it is usually provided from play.api.Play.current.

To provide an environment for tests, Play provides a FakeApplication class which can be configured with a different Global object, additional configuration, or even additional plugins.

val fakeApplicationWithGlobal = FakeApplication(withGlobal = Some(new GlobalSettings() {
  override def onStart(app: Application) { println("Hello world!") }
}))

WithApplication

To pass in an application to an example, use WithApplication. An explicit FakeApplication can be passed in, but a default FakeApplication is provided for convenience.

Because WithApplication is a built in Around block, you can override it to provide your own data population:

abstract class WithDbData extends WithApplication {
  override def around[T: AsResult](t: => T): Result = super.around {
    setupData()
    t
  }

  def setupData() {
    // setup data
  }
}

"Computer model" should {

  "be retrieved by id" in new WithDbData {
    // your test code
  }
  "be retrieved by email" in new WithDbData {
    // your test code
  }
}

WithServer

Sometimes you want to test the real HTTP stack from with your test, in which case you can start a test server using WithServer:

"test server logic" in new WithServer {
  // The test payment gateway requires a callback to this server before it returns a result...
  val callbackURL = "http://" + myPublicAddress + "/callback"

  // await is from play.api.test.FutureAwaits
  val response = await(WS.url(testPaymentGatewayURL).withQueryString("callbackURL" -> callbackURL).get())

  response.status must equalTo(OK)
}

The port value contains the port number the server is running on. By default this is 19001, however you can change this either by passing the port into the with WithServer constructor, or by setting the system property testserver.port. This can be useful for integrating with continuous integration servers, so that ports can be dynamically reserved for each build.

A FakeApplication can also be passed to the test server, which is useful for setting up custom routes and testing WS calls:

val appWithRoutes = FakeApplication(withRoutes = {
  case ("GET", "/") =>
    Action {
      Ok("ok")
    }
})

"test WS logic" in new WithServer(app = appWithRoutes, port = 3333) {
  await(WS.url("http://localhost:3333").get()).status must equalTo(OK)
}

WithBrowser

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

"run in a browser" in new WithBrowser {
  browser.goTo("/")
  browser.$("#title").getTexts().get(0) must equalTo("Hello Guest")

  browser.$("a").click()

  browser.url must equalTo("/")
  browser.$("#title").getTexts().get(0) must equalTo("Hello Coco")
}

Like WithServer, you can change the port, FakeApplication, and you can also select the web browser to use:

"run in a browser" in new WithBrowser(webDriver = FIREFOX) {
  ...
}

PlaySpecification

PlaySpecification excludes some of the mixins provided in the default specs2 specification that clash with Play helpers methods. It also mixes in the Play test helpers and types for convenience.

object ExamplePlaySpecificationSpec extends PlaySpecification {
  "The specification" should {

    "have access to HeaderNames" in {
      USER_AGENT must be_===("User-Agent")
    }

    "have access to Status" in {
      OK must be_===(200)
    }
  }
}

Testing a template

Since a template is a standard Scala function, you can execute it from your test, and check the result:

"render index template" in new WithApplication {
  val html = views.html.index("Coco")

  contentAsString(html) must contain("Hello Coco")
}

Testing a controller

You can call any Action code by providing a FakeRequest:

object FunctionalExampleControllerSpec extends PlaySpecification {

  "respond to the index Action" in new WithApplication {
    val result = controllers.Application.index()(FakeRequest())

    status(result) must equalTo(OK)
    contentType(result) must beSome("text/plain")
    contentAsString(result) must contain("Hello Bob")
  }
}

Testing the router

Instead of calling the Action yourself, you can let the Router do it:

"respond to the index Action" in new WithApplication(fakeApplication) {
  val Some(result) = route(FakeRequest(GET, "/Bob"))

  status(result) must equalTo(OK)
  contentType(result) must beSome("text/html")
  charset(result) must beSome("utf-8")
  contentAsString(result) must contain("Hello Bob")
}

Testing a model

If you are using an SQL database, you can replace the database connection with an in-memory instance of an H2 database using inMemoryDatabase.

val appWithMemoryDatabase = FakeApplication(additionalConfiguration = inMemoryDatabase("test"))
"run an application" in new WithApplication(appWithMemoryDatabase) {

  val Some(macintosh) = Computer.findById(21)

  macintosh.name must equalTo("Macintosh")
  macintosh.introduced must beSome.which(_ must beEqualTo("1984-01-24"))
}

Next: Advanced topics


Found an error in this documentation? The source code for this page can be found here. After reading the documentation guidelines, please feel free to contribute a pull request.