I was very surprised to find out that following simple code example doesn't work for all Mockito versions > 1.8.5
@RunWith(MockitoJUnitRunner.class)
public class MockitoTest {
@Mock(name = "b2")
private B b2;
@InjectMocks
private A a;
@Test
public void testInjection() throws Exception {
assertNotNull(a.b2); //fails
assertNull(a.b1); //also fails, because unexpectedly b2 mock gets injected here
}
static class A{
private B b1;
private B b2;
}
interface B{}
}
In javadocs (http://docs.mockito.googlecode.com/hg/latest/org/mockito/InjectMocks.html) there is a quote:
Note 1: If you have fields with the same type (or same erasure), it's better to name all @Mock annotated fields with the matching fields, otherwise Mockito might get confused and injection won't happen.
Does it mean that if I have several fields with same type I can't mock ONLY ONE of them but rather should define @Mock
for ALL fields with same type?
Is it known limitation and is there any reason why it wasn't fixed yet?
It should be straightforward to match @Mock
by fields names, isn't it?
@Spy and @InjectMocks cannot be used well together (see Google Code issue #489 and GitHub issue #169), and for what they do it is not clear or common that they should be used together at all. In well-written Mockito usage, you generally should not even want to apply them to the same object.
@InjectMocks creates an instance of the class and injects the mocks that are created with the @Mock annotations into this instance. @Mock is used to create mocks that are needed to support the testing of the class to be tested. @InjectMocks is used to create class instances that need to be tested in the test class.
@InjectMocks is the Mockito Annotation. It allows you to mark a field on which an injection is to be performed. Injection allows you to, Enable shorthand mock and spy injections.
@Mock is used to declare/mock the references of the dependent beans, while @InjectMocks is used to mock the bean for which test is being created.
It appears Mockito uses an algorithm described in their JavaDoc
If I understand correctly, it will first sort on type (in this case only 1 B) and then sort on name (no changes here). It will finally inject using the OngoingInjector interface implementation, which appears to search for the first field and inject it.
Since you only have 1 B defined and there are 2 fields of B in the Mock, it will see the match of the first instance to the field and stop. This is because mocks.size() == 1
in NameBasedCandidateFilter
. Therefore it will stop filtering and inject it directly. If you create multiple mocks of the same type, they will get sorted on Name and injected accordingly.
I was able to get it work when I created multiple mocks (but less than the number of fields) of a specific type.
@RunWith(MockitoJUnitRunner.class)
public class MockitoTest {
@Mock(name = "b2")
private B b2;
@Mock(name = "b3")
private B b3;
@InjectMocks
private A a;
@Test
public void testInjection() {
System.out.println(this.a);
}
static class A {
private B b1;
private B b2;
private B b3;
}
interface B {
}
}
This will correctly inject b2 into a.b2 and b3 into a.b3 instead of a.b1 and a.b2 (the first 2 fields that are defined in A).
You can always leave a GitHub issue on their repository with an enhancement or change on the injection filtering algorithm in order to be looked at.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With