我从事Java项目,并开始使用Spock框架在Groovy中编写单元测试。但是我对Spock的嘲弄功能有一个问题,希望有人能弄清楚我做错了什么。
我有三个java类:一个FooContext
(包含foo
属性),一个HasFooContext
类(包含fooContext
属性)和一个{{ 1}}继承自FooService
(并且有一个调用HasFooContext
的操作):
fooContext
此处可以看到public class FooContext {
private Object foo = new Object();
public Object getFoo() {
return foo;
}
}
public abstract class HasFooContext {
private FooContext fooContext;
public void setFooContext(FooContext fooContext) {
this.fooContext = fooContext;
}
public Object getFoo() {
Object foo = fooContext.getFoo();
assert foo != null : "no foo available";
return foo;
}
}
public class FooService extends HasFooContext {
public void doFoo() {
getFoo();
}
}
中的doFoo
方法调用基类FooService
中的getFoo
方法,后者又调用HasFooContext
其getFoo
属性的方法。它还断言从fooContext
返回的值不为空。
我使用Mockito在Java中编写了以下单元测试,以验证调用fooContext.getFoo()
是否会调用doFoo
方法:
fooContext.getFoo()
此测试按预期成功运行。
然后我使用Spock在Groovy中编写了以下单元测试:
public class FooServiceJavaUnitTest {
private FooContext fooContext;
private FooService fooService;
@Before
public void setup() {
Object foo = new Object();
fooContext = mock(FooContext.class);
when(fooContext.getFoo()).thenReturn(foo);
fooService = new FooService();
fooService.setFooContext(fooContext);
}
@Test
public void doFooInvokesGetFoo() {
fooService.doFoo();
verify(fooContext, times(1)).getFoo();
}
}
此测试失败如下:
FooServiceGroovyUnitTest.doFoo调用getFoo:21没有foo可用
也就是说,使用Groovy / Spock时会出现以下情况,但使用Java / Mockito时则不然:
class FooServiceGroovyUnitTest extends Specification {
private FooContext fooContext;
private FooService fooService;
def setup() {
// Create a mock FooContext.
fooContext = Mock(FooContext)
fooContext.getFoo() >> new Object()
fooService = new FooService()
fooService.fooContext = fooContext
}
def "doFoo invokes getFoo"() {
when: "call doFoo"
fooService.doFoo()
then: "getFoo is invoked"
1 * fooContext.getFoo()
}
}
public abstract class HasFooContext {
...
public Object getFoo() {
Object foo = fooContext.getFoo();
assert foo != null : "no foo available";
return foo;
}
}
中的foo
属性不是最终的,因此FooContext
方法不应该是最终的,因此生成的代理应该没有问题拦截该方法(根据Java / Mockito测试)。
请注意,如果我使用具体getFoo()
的间谍替换模拟FooContext
,如下所示:
FooContext
然后Groovy / Spock单元测试通过。这表明在嘲笑具体类与侦察具体类时,行为是不同的。 Spock文档提到Mock api支持两种接口(使用动态代理)和类(使用CGLIB)。
据我所知,Java / Mockito单元测试和Groovy / Spock单元测试是等效的,但是在模拟class FooServiceGroovyUnitTest extends Specification {
...
def setup() {
// Spy on a real FooContext.
fooContext = Spy(FooContext)
fooService = new FooService()
fooService.fooContext = fooContext
}
}
类时我无法通过Groovy / Spock单元测试。
有关我做错的任何建议吗?
答案 0 :(得分:1)
同一个交互的模拟和存根需要在同一个语句中进行:
1 * fooContext.getFoo() >> new Object()
大多数其他Java模拟框架(除了Mockito之外)都以相同的方式运行。有关此行为的更多详细信息,请参阅official documentation。