Spock框架的Mock Verify返回了0次调用

3

我正在开发一个Spring Boot的Java服务,其中包含一个Camel Processor类,如下所示:

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中,该语句被执行,并且当在Eclipse中突出显示logService对象时,它显示为“Mock for type LogService named $spock_sharedField_logService”,因此看起来mock已经成功注入到MyProc实例中。
我对Spock和Groovy都很陌生,所以可能有些地方理解不正确,但是这个测试不应该通过吗?当运行测试时,mocks方法被调用,所以我不明白为什么测试报告说mocks方法根本没有被调用。我初始化mock/将mock设置在MyProc实例上/设置交互是否不正确?我错过了一些Groovy的特性或警告吗?据我所知,Spock mocks将从调用的方法返回默认值,这很好,我只是想检查proc.process中是否调用了这个特定的方法,并且使用了有效的exchange object。

1
作为一般性的注意事项,字段注入存在问题--尽可能使用构造函数注入。 - chrylis -cautiouslyoptimistic-
1
如果你去掉@Shared会发生什么?看起来你只实例化了mock一次(通常每个方法都要重置它),但是每次都重新构建上下文。这种不匹配可能会导致问题。 - chrylis -cautiouslyoptimistic-
放弃共享并切换到void setup() vs setupSpec,谢谢。 - jbailie1991
我还以为每个特征方法都会得到自己的实例字段副本,这难道不意味着每个测试都有一个 proc 和 logService 的新版本? - jbailie1991
我会保持这个窗口开着,稍后再发布。 - chrylis -cautiouslyoptimistic-
显示剩余2条评论
1个回答

5
一个简单的回答是不要在模拟/存根/间谍中使用 @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规范时,Spock为每个测试创建一个Specification实例,并创建一个共享的Specification实例来跟踪共享字段。
查看org.spockframework.runtime.BaseSpecRunner,你会看到两个字段:
protected Specification sharedInstance;
protected Specification currentInstance;

此外,有两个规范上下文:共享和当前。该上下文保留了实例、模拟上下文以及模拟控制器。接下来是问题所在。如果你将模拟声明为共享,则绑定到共享模拟控制器,但是当你声明相互作用(“then:”块)时,Spock会将这些相互作用添加到当前模拟控制器中。因此,当调用模拟方法(例如logService.update(e))时,Spock会检查是否允许使用共享模拟控制器进行此交互,由于你将交互放在当前控制器中,因此找不到任何内容。 不要将模拟/存根/间谍用作@Shared字段。

或者至少是Spock嘲笑。仍然有一些边缘情况(例如Spring上下文注入),您必须使用Mockito mocks。抽搐 - chrylis -cautiouslyoptimistic-

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接