我正在开发包含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的一部分被调用,并带有有效的交换对象
答案 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字段。