New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bug with @InjectMocks in 5.3.0 #2978
Comments
@jfrantzius we have another one here |
@TimvdLippe hm, according to @InjectMocks Javadoc, setter injection shouldn't happen if constructor injection was successful. So I wonder why setter injection is happening here at all? @robertotru did you put a breakpoint or log output in the constructor, so we can be sure that it was called with the two expected arguments? It would be nice if you could put the Java files into a zip that makes this easy to reproduce. |
This is what we have seen, yes. If the mock is created with the constructor injection, field injection is not attempted. But when the initialization is done for the parent class, the mock is already in the context for the constructor injection, and so the field injection is attempted.
Yes, it was. First round, on the test class, used constructor injection. Second round on the parent test class, didn't do that because the mock was already created and just tried to inject the mocks in the fields. @jfrantzius you can clone this repo to reproduce the issue https://github.com/robertotru/MockitoBug2978 |
I think that probably an easy way to fix this is to keep a map of the already initialized mocks and avoid to reinitialize them when a super class is found. But I'm pretty sure this will introduce regressions in those who have tests relying on this feature.
Hence it is not expected that Mockito applies multiple mock initialization strategy on the same field and therefore my approach for tacking this regression should be fine. Let me know what you think. |
Based on the additional explanation, I indeed consider this working as intended. Apologies for the breakage. |
@TimvdLippe I disagree. My English is far from being perfect, but only either I think it means only one of the following strategies, while in the example is using more than one. In addition, the current implementation is not deterministic and can lead to different results based on the test class hierarchy. E.g. In my demo project https://github.com/robertotru/MockitoBug2978 it is sufficient to remove the parent abstract class (that does nothing, i.e. no mocks defined, no junit extensions, nothing) and then the exception will disappear. Hence, please, reconsider the decision of closing the issue. |
There is something weird there, but unfortunately I didn't have the time yet to have a closer look. |
@jfrantzius in case we agree on the expected behavior, I could try to work on a fix (inputs are welcome). Up to you. |
i just tried to update large project from mockito 5.2.0 to 5.3.1 and have probably related or same issue:
ProductOrderUtils is simple class, no interfaces, no inheritance. In our case, all should be injected into private fields (which are normally autowired by spring over field injection). |
I believe this is caused by the same issue as noted in #2602 but just has been exacerbated now since an exception is thrown when the super class fields cannot be properly injected |
Hi @JohnMTu , |
This is what I tried to fix more than a year ago :D But sadly nobody understood my PR I guess. #2602 |
@1-alex98 wow that's a long and winding story with a lot of digging in the history of Mockito! @TimvdLippe I also think that #2602 is the underlying cause |
@robertotru do you agree? |
But I think the fix is super mega easy. See my PR just stop rerunning the DI in this strange loop https://github.com/mockito/mockito/pull/2603/files#diff-98b2e887ea324c1fba28579cc0347aed844875dc56c99886a003551982d00327L56 that does it for the number of super classes the test case has. |
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. |
@TimvdLippe I'll give it a try tomorrow, thanks. |
@TimvdLippe I can confirm that #2603 fixes the problem on our organization project |
Thx a lot @robertotru ! |
@1-alex98 the build is failing on your PR. Do you mind fixing the assertions in the test so that we can merge it? Once again apologies for the delay and thanks for picking this up. |
Thak you guys, this was helpful , We are trying to upgrade spring-boot-starter 2.7.0 to 3.1.5 , as spring-boot-starter-test 3.1.x brings mockito core 5.3.1 and mockito-junit-jupiter 5.3.1 , wanted to know if there is a plan of including mockito core 5.6.0 and mockito-junit-jupiter 5.6.0 in next 3.1.x release(assuming mockito core 5.6.0 have the issue fixes) . This will help us decide whether to upgrade to spirng boot starter 3.1.x or 3.0.x. |
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:
@InjectMocks
.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 classOngoingMockInjection
to add twoinjectionStrategies
:ConstructorInjection
PropertyAndSetterInjection
So, for the first invocation, the method
processInjection
inConstructorInjection
will initialize the class annotated with@InjectMocks
inside helper classFieldInitializationReport
by checking thatPlugins.getMemberAccessor()
inFieldInitializer
has no value for thefieldInstance
(see line 141 and 142 inFieldInitializer
).For the second invocation, since
Plugins.getMemberAccessor()
inFieldInitializer
has already an initialized value for thefieldInstance
, then we return false and enable propagating the execution ofPropertyAndSetterInjection
.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 methodmoreThanOneMockCandidate
.How to reproduce it. Create a JUnit5 project with Mockito 5.3.0
Add the following production code in dedicated Java files
and then in the test folder add dedicated Java files for the following classes
You should get
The text was updated successfully, but these errors were encountered: