문제

I work on a Java project, and have started writing my unit tests in Groovy, using the Spock framework. But I'm having an issue with Spock's mocking functionality, and hoping someone can figure out what I'm doing wrong.

I have three java classes: a FooContext (that contains a foo property), a HasFooContext class (that contains a fooContext property), and a FooService that inherits from HasFooContext (and has an operation that calls the 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();
  }
}

It can be seen here that the doFoo method in FooService calls the getFoo method in the base class HasFooContext, which in turn calls the getFoo method of its fooContext property. It also asserts that the value returned from fooContext.getFoo() is not null.

I wrote the following unit test in Java, using Mockito, to verify that calling doFoo will invoke the fooContext.getFoo() method:

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();
  }

}

This test runs successfully, as expected.

I then wrote the following unit test in Groovy, using Spock:

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()
  }

}

This test fails as follows:

FooServiceGroovyUnitTest.doFoo invokes getFoo:21 no foo available

That is, the following fails when using Groovy/Spock, but not when using Java/Mockito:

public abstract class HasFooContext {
  ...
  public Object getFoo() {
    Object foo = fooContext.getFoo();
    assert foo != null : "no foo available";
    return foo;
  }
}

The foo property in FooContext is not final, so the getFoo() method should not be final, so the generated proxies should have no problem intercepting that method (as per the Java/Mockito test).

Note that if I replace the mock FooContext with spying on a concrete FooContext as follows:

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

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

then the Groovy/Spock unit test passes. Which suggests that the behaviour is different when mocking a concrete class vs spying on a concrete class. The Spock documentation mentions that the Mock api supports both interfaces (using dynamic proxies) and classes (using CGLIB).

As far as I can see, the Java/Mockito unit test and the Groovy/Spock unit tests are equivalent, but I cannot get the Groovy/Spock unit test to pass when mocking the FooContext class.

Any suggestions on what I am doing wrong?

도움이 되었습니까?

해결책

Mocking and stubbing of the same interaction needs to happen in the same statement:

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

Most other Java mocking frameworks (with the notable exception of Mockito) behave in the same way. For more details on this behavior, consult the official documentation.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top