Reputation: 3258
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?
Upvotes: 3
Views: 4509
Reputation: 123890
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.
Upvotes: 1