How do I unit test a controller in play framework 2 scala

playframework-2.0, scala, unit-testing

Solution

Using Mockito with Specs2, I mock services to verify their method calls.

My controller is instantiated by Spring. That allows me to treat it is as a `class` instead of `object`. => That is essential to make `controller` testable. Here an example:

@Controller
class MyController @Autowired()(val myServices: MyServices) extends Controller

To enable Spring for controllers, you have to define a `Global` object, as the Play! documentation explains:

object Global extends GlobalSettings {

  val context = new ClassPathXmlApplicationContext("application-context.xml")

  override def getControllerInstance[A](controllerClass: Class[A]): A = {
    context.getBean(controllerClass)
  }
}

My unit test doesn't need Spring; I just pass collaborators (mocks) to constructor.

However, concerning the rendered template, I test only for the type of result (Ok, BadRequest, Redirection etc...). Indeed, I noticed it's not easy at all to make my test scan the whole rendered template in details (parameters sent to it etc..), with only unit testing.

Thus, in order to assert that the right template is called with the right arguments, I trust my acceptance tests running Selenium, or a possible functional test, if you prefer, to scan for the whole expected result.

2 - The return values from the services are passed to the correct attributes of the template

It's pretty easy to check for that..How? By trusting compiler! Prefer to pass some custom types to your template instead of simple primitives for instance: `phone: String` would become: `phone: Phone`. (a simple value object). Therefore, no fear to pass the attributes in a non-expected order to your template (in unit test or real production code). Compiler indeed will warn.

Here's an example of one of my unit test (simplified) using specs2: (You will note the use of a wrapper: `WithFreshMocks`). This `case class` would allow to refresh all variables (mocks in this case) test after test. Thus a good way to reset mocks.

    class MyControllerSpec extends Specification with Mockito {

      def is =
        "listAllCars should retrieve all cars" ! WithFreshMocks().listAllCarsShouldRetrieveAllCars

      case class WithFreshMocks() {

        val myServicesMock = mock[MyServices]
        val myController = new MyController(myServicesMock)

        def listAllCarsShouldRetrieveAllCars = {
          val FakeGetRequest = FakeRequest() //fakeRequest needed by controller
          mockListAllCarsAsReturningSomeCars()
          val result = myController.listAllCars(FakeGetRequest).asInstanceOf[PlainResult] //passing fakeRequest to simulate a true request
          assertOkResult(result).
            and(there was one(myServicesMock).listAllCars()) //verify that there is one and only one call of listAllCars. If listAllCars would take any parameters that you expected to be called, you could have precise them.
        }

        private def mockListAllCarsAsReturningSomeCars() { 
           myServicesMock.listAllCars() returns List[Cars](Car("ferrari"), Car("porsche"))
        }

        private def assertOkResult(result: PlainResult) = result.header.status must_== 200

       }

Problem

Say I've got a controller with an action that receives two parameters. It invokes two services, one with each parameter, the services both return strings each of those strings are passed as arguments to a template the result is passed to Ok and returned. I want to write a simple unit test that ensures: 1 - The correct services are invoked with the correct parameters 2 - The return values from the services are passed to the correct attributes of the template What is the best way to do that?

Original source