Spock没有正确地嘲笑具体类?

时间:2014-03-05 22:39:10

标签: unit-testing groovy spock

我从事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方法,后者又调用HasFooContextgetFoo属性的方法。它还断言从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单元测试。

有关我做错的任何建议吗?

1 个答案:

答案 0 :(得分:1)

同一个交互的模拟和存根需要在同一个语句中进行:

1 * fooContext.getFoo() >> new Object()

大多数其他Java模拟框架(除了Mockito之外)都以相同的方式运行。有关此行为的更多详细信息,请参阅official documentation