【问题标题】:Unit Test controllers in Play 2 framework with Scala带有 Scala 的 Play 2 框架中的单元测试控制器
【发布时间】:2014-06-12 10:45:23
【问题描述】:

在每个层都应该分开的 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“产品”以将其与数据库隔离?

val products = Product.findAll

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

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

【问题讨论】:

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


    【解决方案1】:

    增加控制器可测试性的推荐方法是使用 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
    

    【讨论】:

    • 为什么会被否决?蛋糕模式甚至在文档的测试页面中使用! playframework.com/documentation/2.3.x/ScalaTestingWithSpecs2
    • 我没有投反对票,但蛋糕(反?)模式是一个装置,与构造函数注入相比并没有真正提供任何好处(当然,意见会有所不同)。 Martin Odersky 在编写 Dotty 编译器时放弃了它:groups.google.com/forum/#!msg/scala-user/n50RW4gQBS4/…
    • @IonuțG.Stan 人们对蛋糕图案有用性的看法并不重要:这并没有改变 Play 文档仍然推荐它并在许多应用程序中成功使用的事实和其他人。这是事实上推荐的方法。不管怎样,谢谢你指点我,这是一本好书!
    • 我认为这很重要,因为他们的建议也是一种意见,只要有可能,就像@vptheron 提到的那样。
    【解决方案2】:

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

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

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

    使用声明为类的控制器,很容易单独对它们进行单元测试,并从不同的层/模块模拟它们的依赖关系。

    编辑:请注意,我确实推荐 Play 文档建议的任何 DI 框架。说真的,只需将您的GlobalSettings 视为main,实例化您的组件并完成它。不要尝试向 Spring、Guice 等人添加另一个无用的依赖项。

    【讨论】:

    • Play 默认编码风格可能不是很好,但是像你建议的那样在运行时(而不是编译时)实例化你的控制器也不是很好。我不能对此表示赞同。
    • @DCKing 你什么时候实例化你的控制器?在开始您的应用之前?
    • 如果您将它们实例化为objects,它们会在启动您的应用程序之前被实例化,否则会出现编译错误。您的建议可能会导致运行时错误。
    • 1) 错误:对象在需要时被实例化,请参阅:stackoverflow.com/questions/6249569/… Play 在传入请求需要它们之前不会实例化它们。 2) 我建议使用onStart 挂钩来实例化资源——包括控制器。您仍然可以只创建一个实例并将其返回以处理相应的请求。
    • 是的,objects 因此在创建Routes 对象时被实例化。但它们的实例化保证会​​成功。
    猜你喜欢
    • 2017-11-14
    • 2016-11-14
    • 1970-01-01
    • 1970-01-01
    • 2020-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多