我从事Java项目,并已开始使用Spock框架在Groovy中编写我的单元测试。但是我对Spock的模拟功能有疑问,希望有人能弄清楚我在做什么错。
我有三个Java类:FooContext
(包含foo
属性),HasFooContext
类(包含fooContext
属性)和从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
方法。它还断言从fooContext.getFoo()
返回的值不为null。我使用Mockito在Java中编写了以下单元测试,以验证调用
doFoo
将调用fooContext.getFoo()
方法: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();
}
}
此测试按预期成功运行。
然后,我使用Spock在Groovy中编写了以下单元测试:
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()
}
}
该测试失败,如下所示:
FooServiceGroovyUnitTest.doFoo调用getFoo:21没有可用的foo
也就是说,使用Groovy / Spock时以下失败,但使用Java / Mockito时失败:
public abstract class HasFooContext {
...
public Object getFoo() {
Object foo = fooContext.getFoo();
assert foo != null : "no foo available";
return foo;
}
}
foo
中的FooContext
属性不是最终的,因此getFoo()
方法不应该是最终的,因此生成的代理在侦听该方法时应该没有问题(根据Java / Mockito测试)。请注意,如果我将 spy
FooContext
替换为监视具体的FooContext
,如下所示:class FooServiceGroovyUnitTest extends Specification {
...
def setup() {
// Spy on a real FooContext.
fooContext = Spy(FooContext)
fooService = new FooService()
fooService.fooContext = fooContext
}
}
然后Groovy / Spock单元测试通过。这表明在 mock 具体类与侦查具体类时的行为是不同的。 Spock文档提到Mock api支持接口(interface)(使用动态代理)和类(使用CGLIB)。
据我所知,Java / Mockito单元测试和Groovy / Spock单元测试是等效的,但是在模拟
FooContext
类时,我无法通过Groovy / Spock单元测试。关于我在做什么错的任何建议?
最佳答案
相同交互的 mock 和存根需要在同一条语句中发生:
1 * fooContext.getFoo() >> new Object()
大多数其他Java模拟框架(Mockito除外)的行为均相同。有关此行为的更多详细信息,请查询official documentation。
关于unit-testing - Spock没有正确 mock 具体的类?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/22210943/