The default testing framework in Play 2 is specs2, however I prefer to use ScalaTest as I quite like it and I’m more familiar with it.
Setting up the sbt project and configuration to make it work, though, turned out to be unnecessarily tricky and cumbersome.
Here is eventually what I did to make it work (the full code is available on my github Sentinel repo).
nuke your ~/.ivy2 local repository
For reasons that largely escape me, specs2 got into my classpath and the Play default test runner was failing with weird ClassNotFound and MethodNotFound errors – it devolved into a full-scale wild goose chase, until I decided to `nuke` (aka, rm -rf) the local Ivy cache (typically, at ~/.ivy2 on a Linux system).
instructions on play users guide are lacking
Or, I should say, insufficient to get you going.
The actual build.sbt file that eventually got me going should look something like this:
libraryDependencies ++= Seq( "org.scalatest" %% "scalatest" % "2.2.1" % "test", "org.scalatestplus" %% "play" % "1.2.0" % "test", cache )
Make sure you check out the Versions Versions Versions page on ScalaTestPlus and pick the right combination, given your favorite choice of Play version (I’m currently using 2.3.0).
get your imports right
This was also source of much unhappiness, as many matchers and DSL constructs were highlighted by IntelliJ as “syntax errors” and were not picked up by the auto-import feature:
import org.scalatestplus.play.{OneAppPerSuite, PlaySpec} import play.api.test.FakeRequest import play.api.test.Helpers._
This has mostly to do with implicits – a Scala feature that I find disturbingly reminiscent of C++ implicit type conversions, which gave me so many hours of unbridled joy when dealing with STL and Boost (oh, those delightful compile error messages…)
fakerequest and friends
To test your API, you need to use FakeRequest; as I need the same headers defined for all calls, I find it useful to define a trait:
trait WithControllerAndRequest { val testController = new Controller with ApiController def fakeRequest(method: String = "GET", route: String = "/") = FakeRequest(method, route) .withHeaders( ("Date", "2014-10-05T22:00:00"), ("Authorization", "username=bob;hash=foobar==") ) }
Generally, you then call your API methods using the apply() method:
val apiResult = testController.orgById(orgId.toString).apply(fakeRequest())
However, if your API response contains a JSON body, the apply(request) will not return a Future[Result] but an Iteratee[Array[Byte], Result]: you should use call() instead:
val apiResult = call(testController.modifyOrg(orgId.toString), request)
See my ApiControllerSpec class for a full set of examples.
Leave a Reply