2
votes

The problem seems really simple in an MVC style where every layer is supposed to be separated. However, when you apply the recommended Play 2 coding style (according to "Play for Scala") then it becomes difficult to Unit test controllers by isolating them.

Here is the code proposed by the authors of "Play for Scala" for a simple controller :

Object product extends Controller {
  def list = Action { implicit request =>
    val products = Product.findAll
    Ok(views.html.products.list(products))
  }
}

How is it possible to mock the DAO "Product" in order to isolate it from the database, with this way of calling for data ?

val products = Product.findAll

Product is not injected it seems nor a simple variable of this object that you could mock.

Did I miss or misunderstood something ? Is there any possibility to Unit test this ? Mocking or any other solution isolating the controller's method ?

2

2 Answers

4
votes

The recommended way to increase the testability of your controllers is to use Scala-flavoured dependency injection. Read up on the cake pattern for more information on how to do that.

In the case of your application, it could look like this.

// ProductController.scala
trait ProductController extends Controller {
  this: ProductComponent =>

  def list = Action { implicit request =>
    val products = Product.findAll
    Ok(views.html.products.list(products))
  }
}

// Somewhere else
trait ProductComponent {
   val products: ProductDao
}

trait AppProductComponent {
   val products = RealProductDao()
}

// controllers.scala
object ProductController extends ProductController with AppProductComponent

// MyTest.scala
trait TestProductComponent {
   val products = MockedProductDao()
}

val productController = new ProductController with TestProductComponent
// Test productcontroller here
2
votes

The "default" coding style as advocated by Play is really bad. As you noticed, everything is an object, therefor it is very difficult to unit test components.

The solution is to use the GlobalSettings object to create controllers as classes instead of objects.

See http://www.playframework.com/documentation/2.3.x/ScalaDependencyInjection to learn about how to control the creation of controllers (this allows you to declare them as classes, taking dependencies in their construtor) and http://www.playframework.com/documentation/2.3.x/ScalaGlobal to use the various hooks (onStart, onStop, etc) to instantiate your resources.

With controllers declared as classes it is easy to unit tests them separately and mock their dependencies from different layers/modules.

Edit: Note that I do not recommend any DI framework like the Play documentation suggests. Seriously, just treat your GlobalSettings as a main, instantiate your components and be done with it. Don't try to add yet another useless dependency to Spring, Guice et al.