I want to test my Play application by providing mock objects during a test. Off the top of my head, there are a few ways to go about this.
- Provide an alternative route files during testing
- Use Dependency Injection, and check for a global value at runtime
I am not sure which is more feasible, or how to go about doing them. Any insight would be greatly appreciated.
There is a third way; create your controller as a class or a trait for testing. Here is a simple example.
Your trait + implementation:
package services
trait MyService {
def getUser(id:String):User
}
class ConcreteService extends MyService {
override def getUser(id:String):User = {
//Do real stuff
}
}
In your controller class:
package controllers
import services._
class Users(service: MyService) extends Controller {
def show(id: String) = Action {
val user = service.getUser(id)
Ok(views.html.user(user))
}
}
object Users extends controllers.Users(new ConcreteService()) {}
Now you can run some unit tests..
package test
import controllers.Users
import play.api.test._
import play.api.test.Helpers._
import org.specs2.mock.Mockito
import org.specs2.mutable.Specification
class UsersSpec extends Specification with Mockito {
val service = mock[MyService]
"Users controller" should {
"list users" in {
//Insert mocking stuff here
val users = new Users(service)
val result = users.show("somerandomid")(FakeRequest())
status(result) must equalTo(OK)
}
}
}
I had the same question, and experimented some options:
- cake pattern
- injection via implicit
- spring
You can see the code on github here.
My solution is similar to Blake's, except I did it without controllers as classes. You can check out the source here.
For reference I have added an example of using Play's plugin framework for injecting mocked objects during testing.
- http://www.underflow.ca/blog/935/mock-dependency-injection-in-play-2-0
Usage of this method a mocked plugin can be provided at test time:
class Test extends Specification {
"application" should {
"load mock dependency" in {
running(TestServer(9000, FakeApplication(
// The plugin at this class replaces the default
additionalPlugins = Seq("test.MockInjector")
)), HTMLUNIT) { browser =>
browser.goTo("http://localhost:9000")
// Test mock controller
}
}
}
}
The MockInjector
can be used to provide controller objects, or any other injectable component.
Note: I wrote the blog entry, and anyone is free to migrate any and all content from the blog to here.