用Spock模拟真实对象的功能

时间:2019-10-15 10:08:31

标签: grails groovy mocking spock

我想为用Grails框架编写的Web应用程序添加一些控制器单元测试。
我需要跳过在控制器中的getMessage()方法中用作实际对象的cancel_request()方法。

class PublicControllerSpec extends Specification implements ControllerUnitTest<PublicController> {

 def "Cancel Request for user"() {
    given:
    controller.getMessage() << Spy(PublicController){
        getMessage(_ as User, _ as String) >> "SuccessMessage"
    }

    when:
    controller.cancel_request()

    then:
    response.redirectedUrl.contains('/public/list')
  }
}

1 个答案:

答案 0 :(得分:1)

免责声明:我对Grails框架没有任何经验,但是曾经使用过Spock。

根据您的问题:

您说:控制器是一个真实的对象,因此应在测试中使用如下代码创建它:

controller = new SomeController(<dependencies>)

但是随后您指定了对真实对象的期望-IMO不能这样工作,您可以仅对在其实现中具有该功能的代理(存根,模拟,间谍)指定期望,真实对象显然没有

所以基本上,您有两种方法:

选项1:间谍控制器

Controller本身必须是Spy:这是包装真实对象(真实控制器)的东西,默认情况下,将所有方法调用委托给该内部真实对象,除非您不“抑制”此功能(在本示例中情况下,您说类似:行为像真实对象,但是如果有人调用getMessage,则返回一条消息,而不是委派给真实对象)。这种方式的明显缺点是被测对象(控制器)变成了间谍,这并不是一个好方法。

选项2:重构代码

我承认,由于我没有Grails的经验,所以这里可能会遗漏一些东西,但是如果控制器是您编写的代码,则可以选择以下更改:

引入一个依赖关系:一个负责消息计算的类,例如:

 interface MessageCalculator {
     String getMessage()
 }

 class MyController {
      MessageCalculator messageCalculator 
      MyController(MessageCalculator messageCalculator) {
         this.messageCalculator = messageCalculator
      }


      public getMessage() {
           messageCalculator.getMessage()
      }
 }

旁注:是的,我知道可以在这里应用AST转换,但这不是问题的主题,因此我将像这样保留代码

现在无论如何,您可以按以下方式重新执行测试:

   def "test me now"() {
      given:
         def msgCalc = Stub(MessageCalculator)
         msgCalc.getMessage() >> "Successfull message"

         def controller = new MyController(msgCalc)
      ...  
   }