使用Scala的Play 2框架中的单元测试控制器

时间:2014-06-12 10:45:24

标签: scala unit-testing model-view-controller playframework playframework-2.0

在MVC风格中,问题似乎非常简单,每个层都应该分开。但是,当您应用推荐的Play 2编码样式(根据“Play for Scala”)时,通过隔离它们来单元测试控制器变得很困难。

以下是“Play for Scala”作者为简单控制器提出的代码:

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

如何通过这种方式调用数据来模拟DAO“Product”以便将其与数据库隔离?

val products = Product.findAll

产品未注入,似乎也不是这个对象的简单变量,你可以模拟。

我是否错过或误解了什么? 是否有可能进行单元测试?模拟或隔离控制器方法的任何其他解决方案?

2 个答案:

答案 0 :(得分:4)

提高控制器可测试性的推荐方法是使用Scala风格的依赖注入。 Read up on the cake pattern了解有关如何执行此操作的详细信息。

对于您的应用程序,它可能看起来像这样。

// 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

答案 1 :(得分:2)

Play提倡的“默认”编码风格非常糟糕。正如您所注意到的,一切都是对象,因此单元测试组件非常困难。

解决方案是使用GlobalSettings对象将控制器创建为类而不是对象。

请参阅http://www.playframework.com/documentation/2.3.x/ScalaDependencyInjection以了解如何控制控制器的创建(这允许您将它们声明为类,在其构造函数中使用依赖项)和http://www.playframework.com/documentation/2.3.x/ScalaGlobal以使用各种钩子({{1 ,},onStart等)来实例化您的资源。

将控制器声明为类,可以很容易地单独测试它们并从不同的层/模块中模拟它们的依赖关系。

编辑:请注意,我推荐任何DI框架,如Play文档所示。说真的,只需将您的onStop视为GlobalSettings,即可实例化您的组件并完成它。不要试图为Spring,Guice等人添加另一个无用的依赖。