首页 文章

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

提问于
浏览
3

我从事Java项目,并开始使用Spock框架在Groovy中编写单元测试 . 但是我对Spock的 Mock 功能有一个问题,希望有人能弄清楚我做错了什么 .

我有三个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();
  }
}

在这里可以看出 FooService 中的 doFoo 方法调用基类 HasFooContext 中的 getFoo 方法,后者又调用其 fooContext 属性的 getFoo 方法 . 它还声明从 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;
  }
}

FooContext 中的 foo 属性不是final,因此 getFoo() 方法不应该是final,因此生成的代理应该没有问题拦截该方法(根据Java / Mockito测试) .

请注意,如果我用一个具体的 FooContext 替换模拟 FooContext ,如下所示:

class FooServiceGroovyUnitTest extends Specification {
  ...
  def setup() {
    // Spy on a real FooContext.
    fooContext = Spy(FooContext)

    fooService = new FooService()
    fooService.fooContext = fooContext
  }
}

然后Groovy / Spock单元测试通过 . 这表明在嘲笑具体类与侦察具体类时,行为是不同的 . Spock文档提到Mock api支持两个接口(使用动态代理)和类(使用CGLIB) .

据我所知,Java / Mockito单元测试和Groovy / Spock单元测试是等效的,但是在模拟 FooContext 类时我无法通过Groovy / Spock单元测试 .

对我做错了什么建议?

1 回答

  • 1

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

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

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

相关问题