In mockito <=2.0.12 there was possible to use @InjectMocks on spy object, but seems like this was changed since 2.0.13-beta version:
Argument should be a mock, but is: class mockito.bug.snippet.ArgumentShouldBeAMockButIsTest$B
Test case:
package mockito.bug.snippet;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
public class ArgumentShouldBeAMockButIsTest {
// Inject here
static class A {
private B b;
}
// Inject here also
static class B {
private C c;
}
// Some dependency
static class C {
}
@InjectMocks
private final A a = new A();
@Spy
@InjectMocks
private final B b = new B();
@Mock
private C c;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
@Test
// Worked in 2.0.12-beta
public void testArgumentShouldBeAMockButIsNonMock() {
b.toString();
}
}
Using the real objects PR, I was able to create a fix for it.
The problem was an internal name-retrieval of mocks during injection to fix a previous issue. The commit that caused this bug was: https://github.com/mockito/mockito/commit/110ffa80070bf54ab8efabdaaa27b193a93d6128
Looking at that again it seems the use case is a bit adge case, and worked by chance, Mockito injection was never designed to support complicated injection stuff, like building a graph.
I've just noticed that there is still this misbehavior (undocumented feature) when you provide spy object by hand. If you run test provided by @vbuell with object created by Mockito#spy method then test passes (of course it's not important if there is @Spy annotation on that field).
I've reproduced this issue on latest Mockito version: 2.7.14
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
public class ArgumentShouldBeAMockButIsTest {
// Inject here
static class A {
private B b;
}
// Inject here also
static class B {
private C c;
}
// Some dependency
static class C {
}
@InjectMocks
private final A a = new A();
@Spy
@InjectMocks
private final B b = Mockito.spy(new B());
@Mock
private C c;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
@Test
//Test passed
public void testArgumentShouldBeAMockButIsNonMock() {
b.toString();
}
}
So what is Mockito official stance on using @InjectMocks with @Spy? The javadoc still references it as being supported (https://github.com/mockito/mockito/blob/release/3.x/src/main/java/org/mockito/InjectMocks.java#L140) but it doesn't work by using both annotations on a field. I don't mind doing the workaround mentioned above with Mockito.spy(new B()) instead of the annotations while this bug is being fixed but I'd like a definitive answer since that bug has been opened for a while now!
Thanks!
Most helpful comment
So what is Mockito official stance on using
@InjectMockswith@Spy? The javadoc still references it as being supported (https://github.com/mockito/mockito/blob/release/3.x/src/main/java/org/mockito/InjectMocks.java#L140) but it doesn't work by using both annotations on a field. I don't mind doing the workaround mentioned above withMockito.spy(new B())instead of the annotations while this bug is being fixed but I'd like a definitive answer since that bug has been opened for a while now!Thanks!