Groovy Spock-模拟外部服务

Groovy Spock-模拟外部服务,groovy,mocking,spock,grails-services,grails-test,Groovy,Mocking,Spock,Grails Services,Grails Test,我是spock框架测试的新手,没有找到任何可以找到所需信息的示例。因此,我认为最好的方式是展示一个我需要的例子 e、 g.斯波克的测试等级: def "getData" (){ // this is test of getData method from ExternalService when: Result result = externalService.getData() then: result.msg = 'SUCCESS' } 服务类别: public

我是spock框架测试的新手,没有找到任何可以找到所需信息的示例。因此,我认为最好的方式是展示一个我需要的例子

  • e、 g.斯波克的测试等级:

    def "getData" (){ // this is test of getData method from ExternalService
      when:
        Result result = externalService.getData()
      then:
        result.msg = 'SUCCESS'
    }
    
  • 服务类别:

    public class ExternalService(){
      private ServiceConnector serviceConnector;
    
      public Result getData(){
        Result result = serviceConnector.callAndGet(); 
        prepareInformation(data);
        updateStatuses(data);
        return result;
      }
    }
    
  • 将数据作为域类进行分类:

    public class Data {
      private String msg
      private int Id
      // +getters/setters
    }
    
  • 现在我有了
    getData
    测试,并且想模拟唯一的方法
    callAndGet()
    。这意味着每次调用
    callAndGet
    时,我都需要使用msg SUCCESS获得对象数据,但是
    getData
    方法中的所有其他方法都应该正常调用


    这完全可以理解吗?问题是我们如何将服务类
    ExternalService
    注入/模拟到spock测试类中?

    您需要做的是模拟
    ServiceConnector
    类并通过构造函数传递它(例如)。见下文:

    @Grab('org.spockframework:spock-core:1.0-groovy-2.4')
    @Grab('cglib:cglib-nodep:3.1')
    
    
    import spock.lang.*
    
    class Test extends Specification {
        def 'some spec'() {
            given:    
                def serviceConnector = Mock(ServiceConnector) {
                    callAndGet() >> new Result(msg: 'SUCCESS')
                }
                def externalService = new ExternalService(serviceConnector)
    
            when:
                Result result = externalService.getData()
    
            then:
                result.msg == 'SUCCESS'
        }
    }
    
    public class ExternalService {
      private ServiceConnector serviceConnector
    
      public ExternalService(ServiceConnector serviceConnector) {
          this.serviceConnector = serviceConnector
      }
    
      public Result getData() {
        Result result = serviceConnector.callAndGet()
    
        prepareInformation(result)
        updateStatuses(result)
        result
      }
    
      private void prepareInformation(Result data) {
      }
    
      private void updateStatuses(Result data) {
      }
    }
    
    public class ServiceConnector {
        public Result callAndGet() {
    
        }    
    }
    
    public class Result {
        String msg
    }
    
    你不应该试图嘲笑你的服务“只有一种方法”。这只是一个糟糕设计的标志,你的代码是不可测试的。您应该更好地用小型服务隔离类的依赖关系,并在单元测试中模拟这些服务。然后用集成测试和依赖项的完整实现测试上层

    在您的示例中,
    ServiceConnector
    应该是一个接口,可以很容易地进行模拟。在这种情况下,测试可以写成:

    def "test a mocked service connector"() {
      given: "a service connector"
        def serviceConnector = Mock(ServiceConnector)
    
     and: "an external service"
       def externalService = new ExternalService(serviceConnector:serviceConnector)
    
     when: "Data is loaded"
       def result = externalService.data
    
     then: "ServiceConnector is called"
       serviceConnector.callAndGet() >> new Result(msg:"SUCCESS")
    
     and: "Data is mocked"
       result.msg == "SUCCESS"
    }
    
    但是,如果ServiceConnector是一个类,并且您不能更改它,那么您可以在Spock中使用部分模拟。这种测试很难维持,并且会产生很多副作用:

    def "test a mocked service connector"() {
      given: "a service connector"
        def serviceConnector = Spy(ServiceConnector) {
          callAndGet() >> new Result(msg:"SUCCESS")
        }
    
     and: "an external service"
       def externalService = new ExternalService(serviceConnector:serviceConnector)
    
     when: "Data is loaded"
       def result = externalService.data
    
     then: "Data is mocked"
       result.msg == "SUCCESS"
    }