Let’s Play! - My Play Framework Learning Journey (4) - Testing Play Applications

In this article I will be talking about things to take note when testing a Play application.

If you uses constructor injection for the classes you write, testing them will be easy because they are just POJO classes. You can directly instantiate the class and pass in all the dependencies to the constructor. For example:

val repo: CurrencyRepo = mock[CurrencyRepo]
val service: CurrencyService = new CurrencyService(repo)
...

Using GuiceApplicationBuilder

Alternatively, you can define a test module to provide the bindings required for testing. You can then use the GuiceApplicationBuilder class provided by Play to create an play.api.Application and use it for testing:

val repo = mock[CurrencyRepo]
val testModule = new AbstractModule with ScalaModule {
  override def configure() = {
    bind[CurrencyRepo].toInstance(repo)
  }
}
val application = new GuiceApplicationBuilder().bindings(testModule).build()

You can then get the object instance that you want to test from the application’s injector.instanceOf method, and all the dependencies will be taken care for you by Guice:

running(application) {
  val controller = application.injector.instanceOf[CurrencyController]
  val result = controller.getAll().apply(FakeRequest())
  status(result) mustBe httpStatus
  contentType(result) mustBe Some("application/json")
  contentAsJson(result).as[Seq[Currency]] mustBe currencies
}

Play provides a set of helper methods called “running” defined in play.api.test.PlayRunners. “Running” takes in a block of code and executes that block.

Method status, contentType and contentAsJson are useful methods from play.api.test.Helpers that exposes the different parts of the response for verification.

The above example tests the controller by calling its method directly. Alternatively, you can also invoke the endpoint defined via play.api.test.RouteInvokers.route method. For example:

val result = route(application, FakeRequest("POST", "/url-path/").withBody(body)).get

You need to specify the actual web service URL path with any parameters required in the FakeRequest in this case.

Calling Action.apply()

Take a deeper look at this line:

val result = controller.getAll().apply(FakeRequest())

Method getAll is defined as follows:

def getAll: Action[AnyContent] = {
  Action.async {
    repo.findAll map { result => Ok(Json.toJson(result))}
  }
}

Take note that Action[A] has two apply methods. One accepts Request[A] and returns Future[Result]. The other accepts a RequestHeader and returns an Accumulator[ByteString, Result]. The Accumulator is basically a sink that accepts the body as a stream of ByteString and returns the result as a Future when run.

In your test, you should usually call the method that takes Request[A] and returns Future[Result], unless you are attempting to test parsing a raw stream as the body. Pay attention to ensure the right apply() method is called.

Dealing with Json data in Request Body for Testing

Refer to this blog for more about how to use JsonBodyParser to convert objects to Json data required in request body during testing.

Getting and Verifying Json data in Response Body

As mentioned above, Play provides a contentAsJson method in play.api.test.Helpers class to expose the content in Json format (returns a JsValue). You can then convert Json to the desired object type via “JsValue.as[T]” method, given an implicit Format is in scope. Example:

val result = controller.methodA.apply(...)
contentAsJson(result).as[TypeA] mustBe TypeA(...)

Testing Async methods

Many Play and Slick methods are asynchronous and return Future objects. ScalaTest provides Async*Spec for testing with Future objects. For example, if you have a Repo or Service method that returns a Future:

private[domain] def methodA(...): Future[BigDecimal]

You can test it as follows:

class ServiceATest extends AsyncWordSpec with ... {
  ...
  service.methodA(...) map { _ shouldBe 100.32 }
}

Additional References

Go Top
comments powered by Disqus