mockito: Bug with @InjectMocks in 5.3.0
Hello,
With 5.3.0, due to https://github.com/mockito/mockito/pull/2942/commits, a regression was introduced. I investigated the issue with my colleague @sftwr-ngnr (thanks Urs for pairing)
The issue happens when:
- You have a parent class for your test classes.
- You have a class:
- Initialized using
@InjectMocks
. - Some fields are injected through the constructor and other fields are not.
- There are multiple candidate mocks for the field not initialized in the constructor.
- Initialized using
In this case, since we have a parent test class, you will run the initialization twice. Consider that, everytime that you call MockInjection
(one per test class, the actual and the parent), you use the inner class OngoingMockInjection
to add two injectionStrategies
:
ConstructorInjection
PropertyAndSetterInjection
So, for the first invocation, the method processInjection
in ConstructorInjection
will initialize the class annotated with @InjectMocks
inside helper class FieldInitializationReport
by checking that Plugins.getMemberAccessor()
in FieldInitializer
has no value for the fieldInstance
(see line 141 and 142 in FieldInitializer
).
For the second invocation, since Plugins.getMemberAccessor()
in FieldInitializer
has already an initialized value for the fieldInstance
, then we return false and enable propagating the execution of PropertyAndSetterInjection
.
Here it comes the problem. With the changes in https://github.com/mockito/mockito/pull/2942/commits, the TypeBasedCandidateFilter
is triggered to find for all the remaining fields not set via constructor any candidate mock to be set. If there is more than one, it throws the exception from method moreThanOneMockCandidate
.
How to reproduce it. Create a JUnit5 project with Mockito 5.3.0 Add the following production code in dedicated Java files
public interface Dependency {
}
public interface Dependency1 extends Dependency{
}
public interface Dependency2 extends Dependency{
}
public class Service implements Dependency {
private Dependency dependency;
private final Dependency1 dependency1;
private final Dependency2 dependency2;
public Service(Dependency1 dependency1, Dependency2 dependency2) {
this.dependency1 = dependency1;
this.dependency2 = dependency2;
}
void postConstruct() {
dependency = null; // it could be something else, like a call to a context or to new
}
}
and then in the test folder add dedicated Java files for the following classes
public abstract class AbstractTest {
}
@ExtendWith(MockitoExtension.class)
class ServiceTest extends AbstractTest {
@Mock
Dependency1 dependency1;
@Mock
Dependency2 dependency2;
@InjectMocks
Service service;
@Test
void injectionFails() {
Assertions.assertNotNull(service);
}
}
You should get
Mockito couldn't inject mock dependency on field 'private org.example.Dependency org.example.Service.dependency' that is annotated with @InjectMocks in your test,
because there were multiple matching mocks (i.e. fields annotated with @Mock and having matching type): dependency1, dependency2.
If you have multiple fields of same type in your class under test then consider naming the @Mock fields identically to the respective class under test's fields, so Mockito can match them by name.
org.mockito.exceptions.base.MockitoException:
Mockito couldn't inject mock dependency on field 'private org.example.Dependency org.example.Service.dependency' that is annotated with @InjectMocks in your test,
because there were multiple matching mocks (i.e. fields annotated with @Mock and having matching type): dependency1, dependency2.
If you have multiple fields of same type in your class under test then consider naming the @Mock fields identically to the respective class under test's fields, so Mockito can match them by name.
at app//org.mockito.junit.jupiter.MockitoExtension.beforeEach(MockitoExtension.java:153)
About this issue
- Original URL
- State: closed
- Created a year ago
- Reactions: 4
- Comments: 21 (18 by maintainers)
Thx a lot @robertotru !
@TimvdLippe I can confirm that #2603 fixes the problem on our organization project
Given the feedback here, I think #2603 is what we want as well. Can anybody affected by this bug check out the code in #2603, build it locally and confirm that it fixes the issue for you?
We do want to fix InjectMocks, but the challenge is finding the correct small fix for it. We don’t want to massively expand the implementation for this feature, with the risk of introducing even more subtle behavioural changes.
@robertotru do you agree?