在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
产品未注入,似乎也不是这个对象的简单变量,你可以模拟。
我是否错过或误解了什么? 是否有可能进行单元测试?模拟或隔离控制器方法的任何其他解决方案?
答案 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等人添加另一个无用的依赖。