使用 Spock 进行 Grails 测试 - 选择哪个模拟框架?

Posted

技术标签:

【中文标题】使用 Spock 进行 Grails 测试 - 选择哪个模拟框架?【英文标题】:Grails Testing with Spock - Which Mocking Framework Select? 【发布时间】:2013-07-23 15:30:30 【问题描述】:

我有更一般的问题。在使用 Spock 时,我应该使用哪个框架或实现在 Grails 2.x 中进行模拟?

我知道大量的模拟风格:利用 Groovy 元类、Grails mockFor()、Groovy Mock()、Groovy 闭包风格等。它们各有优缺点。但我不明白的是,某些模拟风格在我无法确定的某些情况下有效(即 mockFor() 适用于某些实现而不适用于其他实现)。

目前我有两个类似的服务方法模拟实现。

这个有效:

@TestFor(MyController)
@Mock([MyDevice])
class MyControllerSpec extends ControllerSpec 

void "test st."() 
      def myService = mockFor(MyService)
      myService.demand.myMethod()  def st ->
         return "test"
      
      controller.myService = myService.createMock()


但是,这个实现不起作用:

@TestFor(MyController)
@Mock([MyDevice])
class MyControllerSpec extends ControllerSpec 

void "test st."() 
      def yourService = mockFor(YourService)
      yourService.demand.yourMethod()  def st ->
         return "test"
      
      controller.yourService = yourService.createMock()


服务实现和从控制器调用非常相似。那么在 Grails 中模拟的最佳实践是什么?或者有没有什么好的 Grails 模拟框架可以节省我弄清楚如何模拟的时间?

感谢您的建议! :-)

马特奥

【问题讨论】:

你试过Spock的模拟框架了吗?这真的很简单明了。 spock 文档说它可以与 spock 模拟或 Groovy 模拟一起使用,但警告不要出于某种原因尝试组合这两个模拟框架。 是的,这实际上是你调用 mockFor(); 时将要使用的内容; grails.plugin.spock.UnitSpec.mockFor() 被调用。我发现最有用的是直接使用元类编程来模拟和常规闭包。唯一的问题是,当您不在威胁下降部分清理它时,metaClass 可能会干扰其他测试。也许 Grails 2.3 会带来更好的支持,因为 Spock 将是此版本的默认设置... 如果答案合适并符合您的期望,请接受它以帮助其他人从您的问题中学习。 【参考方案1】:

当您使用 spock 框架进行测试时,请尝试利用框架本身提供的选项和样式。

Spock 框架有助于实现 BDD [行为设计开发]。我的意思是行为,您可以将业务接受场景与开发周期紧密结合。

我尝试用 Spock 编写您的测试用例,因为它可以重写为:

@TestFor(MyController)
class MyControllerSpec extends ControllerSpec 

    void "test service method in called once and only once"()
        //Defines the behavior of setup
        setup: "Only one invocation of service method" 
            def myService = Mock(MyService)
                //Make sure serviceMethod is called only once from the controller
                //Beauty of Spock is that you can test the cardinality of
                //method invocations.  
                1 * serviceMethod() >> "Hello"
            
            controller.myService = myService

            //The above process can be followed 
            //for stubbing/mocking 'YourService' and 
            //then injecting it to controller like
            //controller.yourService = yourService

        //Defines the behavior of action
        when: "controller action is called" 
            controller.myAction()

        //Defines the behavior of expected results
        then: "Expect the stubbed service method output" 
            controller.response.contentAsString == "Hello"
    


//Controller
def myAction() 
    render myService.serviceMethod()

如果您在上面看到,您定义了每个步骤中的行为。从企业的角度来看,这些行为将由 BA 或利益相关者驱动。通过这种方式,您可以遵守 ATDD/BDD(Acceptance Test Driven Dev/Behavior Test Driven Dev),并最终为您的项目遵守 TDD(Test Driven Dev)。

有关如何有效使用 spock 框架的更多详细信息,请访问 spock 框架docs。

【讨论】:

以上是关于使用 Spock 进行 Grails 测试 - 选择哪个模拟框架?的主要内容,如果未能解决你的问题,请参考以下文章

Grails Spock单元测试需要模拟事务管理器

如何在 Grails 单元测试中使用 Spock 模拟 passwordEncoder

IntelliJ IDEA 的零覆盖:带有 Spock 单元测试的 Grails

如何在 Spock 控制器测试中模拟 Grails 请求对象方法

在 Intellij Idea 中运行 grails 2.1.3 测试:Spock 测试中出现奇怪错误:无法添加域类 [class x.y.Z]。它不是域

Grails 2.3 使用 IVY 解析器进行单元测试