I have this weird situation that Mockito is replacing already mocked objects with new ones after calling openMocks()
for @InjectMocks
annotated object.
Here is my setup:
@RunWith(MockitoJUnitRunner.class) public class MyTest {
@Mock ClassA mockClassA;
@Mock ClassB mockClassB;
@InjectMocks ClassToTest classToTest;
AutoCloseable closeable;
@Before public void openMocks() {
closeable = MockitoAnnotations.openMocks(this);
}
//...
}
public class ClassToTest {
@Inject private ClassA classA;
private final ClassB classB;
@Inject public ClassToTest(ClassB classB) {
this.classB = classB;
}
}
P.S. I don't have control over ClassToTest
, I can't rewrite it to move the private member classA
to the constructor.
Here I want to mock both classA
and classB
, while the test is for ClassToTest
. Since classA
is not set via constructor, I need to use @InjectMocks
for Mockito to set it for me. And it did set it correctly with mockClassA
I have in the test.
However, the problem is with classB
. I set a breakpoint before openMocks()
, there I could see classToTest.classA
= null
and classToTest.classB
= mockClassB
, which is expected. Then after calling openMocks()
, I could see classToTest.classA
= mockClassA
, which is nice. But classToTest.classB
now becomes a new mock object not mockClassB
anymore. Since I need to setup the mock returns for mockClassB
, replacing it with a new mock is not going to work.
Is there a way to tell Mockito not to swap classB
with a new mock?
@RunWith(MockitoJUnitRunner.class)
should already initialize all @Mock
-and @InjectMocks
-annotated fields.
MockitoAnnotations.openMocks
is only required if you do not use the annotation and must manually initialize the fields.
@RunWith(MockitoJUnitRunner.class)
public class MyTest {
@Mock ClassA mockClassA;
@Mock ClassB mockClassB;
@InjectMocks ClassToTest classToTest;
@Before public void setup() {
when(mockClassB.someCall()).thenReturn(whatever);
}
@Test public void test() {
// use this.classToTest
}
}
It might be possible to do it without the @Mock
annotations by assigning the fields directly:
@RunWith(MockitoJUnitRunner.class)
public class MyTest {
ClassA mockClassA = mock(ClassA.class);
ClassB mockClassB = mock(ClassB.class);
@InjectMocks ClassToTest classToTest;
@Before public void setup() {
when(mockClassB.someCall()).thenReturn(whatever);
}
@Test public void test() {
// use this.classToTest
}
}
or
public class MyTest {
ClassA mockClassA = mock(ClassA.class);
ClassB mockClassB = mock(ClassB.class);
@InjectMocks ClassToTest classToTest;
AutoCloseable closeable;
@Before public void setup() {
closeable = MockitoAnnotations.openMocks(this);
when(mockClassB.someCall()).thenReturn(whatever);
}
@Test public void test() {
// use this.classToTest
}
}
Alternatively, create all mock objects manually and use reflection to assign the fields of your class under test.
Of course it always pays off to design your classes to be testable, which means that their dependencies are passed via the constructor.
This is similar to Why are my mocked methods not called when executing a unit test? (distinct references to two distinct mock objects, with your test referencing one and your class referencing a different one).