Spock模拟验证返回0次调用

时间:2016-12-14 17:22:36

标签: java unit-testing spring-boot apache-camel spock

我正在开发包含Camel Processor类的Spring Boot java服务,如下所示:

public class MyProc implements Processor {

    @Autowired
    private LogService logService;

    public void process(Exchange e) {
            // exchange object processing
            logService.update(e)
        }   
}

我有以下Spock测试:

class MyProcTest extends Specification {
    @Shared def logService = Mock(LogService)
    @Shared def proc = new MyProc()

    def ctx = new DefaultCamelContext()
    def exch = new DefaultExchange(ctx)

    void setupSpec() {
        proc.logService = logService
    }

    def "log is updated when valid exchange is processed"() {
        given:
            exch.getIn().setBody(//xml string set on body)
        when:
            proc.process(exch)
        then: 
            1 * logService.update(_)
    }
}

当我运行它时,我得到一个失败,说明1 * logService.update(_)(0调用)的调用太少。我尝试调试代码,在MyProc中,语句被命中,而突出显示的logService对象(在Eclipse中)表示'模拟名为$ spock_sharedField_logService'的类型LogService,因此看起来模拟已经成功注入MyProc实例。

我是Spock和Groovy的新手,所以我可能会遗漏一些东西,但不应该通过测试吗?运行测试时会调用mocks方法,所以我不明白为什么测试报告了mocks方法根本没有被调用。我是在初始化模拟/设置MyProc实例上的模拟/设置错误的交互吗?是否有一些我错过的Groovy功能或警告?根据我的理解,Spock mocks将在调用时从方法返回一个默认值,这很好,我只想检查这个特定方法是否已作为proc.process的一部分被调用,并带有有效的交换对象

1 个答案:

答案 0 :(得分:4)

一个简单的答案是不要将@Shared用于模拟/存根/间谍。

@Shared def proc = new MyProcessor()

def test() {
    given:
    def log = Mock(LogService)
    proc.logService = log

    when:
    proc.process(new Object())

    then:
    1 * log.update(_)
}

现在解释: 运行规范时,Spock为每个测试创建一个Specification实例,并创建一个共享的Specification实例来跟踪共享字段。

查看org.spockframework.runtime.BaseSpecRunner,你会看到两个字段:

protected Specification sharedInstance;
protected Specification currentInstance;

此外,有两种规范上下文:共享和当前。上下文保留实例,模拟上下文和模拟控制器。这就是问题所在。当您将模拟声明为共享时,它将绑定到共享模拟控制器,但是当您声明交互时('then:'block),Spock会将这些交互添加到当前模拟控制器。因此,当调用mock方法时(例如logService.update(e)),Spock会检查共享模拟控制器是否允许此交互,因为您将模拟声明为共享但在那里找不到任何内容,因为您将交互放在当前控制器中。

不要将mock / stubs / spies用作@Shared字段。