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.

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)

Most upvoted comments

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?