Exploring the Play Framework: Testing

by
Tags: , , ,

This is the first in a series of posts in which we will explore building applications with the Play Framework. Today we will focus on creating a very simple barebones application and applying some tests to it.

Starting A Skeleton Application

Complete sample code for this article can be found at play-samples/minimal.
The only prerequisite for following along with this article is to have a recent version of sbt installed.
We’ll start by creating a project directory called play-minimal. Since we’re using sbt as our build tool, we’ll create a build.sbt file in the root directory of the project.

    name := "play-minimal"
    version := "1.0"
    scalaVersion := "2.11.4"
    lazy val root = (project in file(".")).enablePlugins(PlayScala)

This basic file sets the name of our project as well as it’s version. It also declares that we want to use Scala 2.11.4 and that we want to use the PlayScala plugin for sbt. The PlayScala plugin provides the infrastructure for us to develop and run our application within sbt. In addition to this, we need to create a folder called project, which will contain a plugins.sbt file:

    resolvers += "Typesafe repository"
              at "http://repo.typesafe.com/typesafe/releases/"
    addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.4")

The plugins.sbt file contains a dependency for the Play sbt plugin, as well as adding the Typesafe repository which hosts the plugin. At this point, running the command sbt run at the command prompt should start the Play server. It will not actually do anything, however, because we have not defined any routes.

Adding A Route

Play uses a file called routes in the conf directory of the project to map HTTP requests to something that will handle the request. We’ll create that file with the following content:

    GET / controllers.Assets.at(path="/public", file="index.html")

This maps an HTTP GET request with the path "/" to a controller called Assets provided by Play. Controllers in Play are the primary handler of HTTP requests. The Assets controller provides convenience methods for handling static content in our application. – files that will be sent directly to the web browser. In this case, we are saying that the request for "/" should be handled by returning the file index.html, which is located in the public directory in the project.

Now, we need to create public/index.html so that there is some content to be returned. This will be your basic Hello World page.

      

Hello World!

Once these two files are created, we can run the application (if it’s not already running) with ‘sbt run’. Once we see the following message

    --- (Running the application from SBT, auto-reloading is enabled) ---
    [info] play - Listening for HTTP on /0:0:0:0:0:0:0:0:9000
    (Server started, use Ctrl+D to stop and go back to the console...)

we can open up a web browser and navigate to http://localhost:9000/. If all goes well, we should see “Hello World!” in bold.

Test It!

Okay, so we have a functional web app, but we really need to get some test code written. We’ll put our test code in the test/scala directory of the project. We’ll use ScalaTest to write our test cases, but specs2 could easily be used as well. In fact, it would be easier to use specs2 because Play provides better built-in support for it. But I like to do things the hard way. 🙂

Here’s a basic test that verifies that we get the expected content.

class AppSpec extends FlatSpec with Matchers
                               with BeforeAndAfterAll
                               with ScalaFutures {
  implicit lazy val app: FakeApplication = new FakeApplication()
  implicit def resultToString(result: Result) =
       new String(await(
        result.body |>>> Iteratee.consume[Array[Byte]]()))
  "The application" should "return index.html on a GET to /" in {
    val routeMaybe = route(FakeRequest(GET, "/"))
    routeMaybe shouldBe 'defined
    whenReady(routeMaybe.get) { result =>
      resultToString(result) should include("<h3>Hello World!")
    }
  }
  override protected def beforeAll() = Play.start(app)
  override protected def afterAll() = Play.stop()
}

Play’s test API provides FakeApplication, which creates our application with default settings. This is very much like running the sbt run command that we used earlier to start up an application. In the beforeAll and afterAll methods we start and stop the application.

In the actual test, we use FakeRequest (again from the Play test API) to make a GET request to "/". We are not actually testing the networking stack here, so there is no need for hostname and port. The FakeRequest is passed to the route function, which submits the request to Play’s router for evaluation. The return value from route() is an Option[Future[Result]]. That Option part tells us that there may not be a matching route defined, in which case a value of None is returned. Assuming that a matching route is defined, the corresponding action will be called, and a Some[Future[Result]] is returned.

In this test we expect that a route is defined, so we’ll start by testing that the result of calling route() is defined (i.e., it is a Some). ScalaTest allows us to pass a symbol value to shouldBe. It will take the string value of the symbol and look for a method called defined or isDefined that returns Boolean. It will call that method and expect it to return true, indicating that the Option is defined.

Assuming that passes, we then need to wait for the Future[Result] to complete. ScalaTest’s ScalaFutures trait provides a method called whenReady, which takes a Future as its first argument and a test function as its second argument. When the Future completes, ScalaTest will execute the test function, passing it the value of the completed Future. That function then checks that the body of the document contains the expected value. Should the Future passed to whenReady not complete within a timeout value, the test will fail.

Finally, we can run the test by executing sbt test at the command line. sbt should compile and run the test suite and report whether it was successful.

I have illustrated here a raw way of testing Play with ScalaTest. I should point out that there is a ScalaTest library called ScalaTest + Play specifically targeted at testing Play apps. The library provides helper traits that take care of the mechanics of starting and stopping Play among other things.

More advanced samples (using ScalaTest + Play) can be found in the reactive-quizzo project at chariotsolutions/reactive-quizzo-code-sample.