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
mockito-core MockitoAnnotations::openMocks initializes class twice if said class has super class #2602
Comments
If you add your use case as a regression test to https://github.com/mockito/mockito/tree/main/subprojects/junit-jupiter/src/test/java/org/mockitousage and all tests pass with your fix, it is safe to merge 😄 Good catch! It's definitely worth fixing. Looking forward to your PR! |
The issues seem to come from this PR long ago #1810 The author does in contradiction to the docs have the wish to inject fields that are not initialized by the constructor via field injection. I think that this "feature" is problematic. Not only does this feature seem to be completely wrong implemented to me, it might causes issues if it was correctly implemented. To me this feature is highly undesired. Here some examples
Mockito would try to inject Mocks into instance variables of the Thread class(which will make it crash).
Mockito would try to inject random Mocks into Object field. |
What happened here if I should guess is the following.
If you ask me: |
Add regression test that makers sure constructor injection is used when possible Fixes mockito#2602
@TimvdLippe what do you think? |
…tionIsUsedIfValidConstructorExists
I wanted to chime in on this as it seems this issue and the combination of #2942 may have made @Injectmocks unusable in a larger than intended number of cases. The root of the issue is that when the field injection occurs on a super class and the super class has a loosely typed field such as object. The mocking will completely fail because all mocks will match this field in the super class. So this forces people to mock all fields in the entire hierarchy of the class even if constructor injection succeeds and is preferred which is counter to the documentation as mentioned above. |
…tionIsUsedIfValidConstructorExists
Add regression test that makers sure constructor injection is used when possible Fixes mockito#2602
Add regression test that makers sure constructor injection is used when possible Fixes mockito#2602
Short, Self Contained, Correct (Compilable), Example:
Have a look here .
You will find a
Works
class and aDoesNotWork
class. Both are essentially the same with a small difference being theDoesNotWork
class does fail and extends and empty class.DoesNotWork.java:
ToBeInitialized.java:
Reading the docs that should work flawlessly. But it does not. As can be seen in
Works.java
the reason is theextends ToBeExtended
if you remove it everything works.Why does it not work
In the
DoesNotWork
the@InjectMocks
annotated instances are initialized twice. It does it twice, one time more for every super class there is. First the@InjectMocks
annotated instances are initialized via constructor injection. In a second round Mockito realizes that DoesNotWork.toBeInitialized is already initialized and therefore chooses Field injection over constructor injection. That makes sense if somebody does something like:The code responsible
InjectingAnnotationEngine::processInjectMocks
mockito/src/main/java/org/mockito/internal/configuration/InjectingAnnotationEngine.java
Line 56 in caf35b2
The method is called by the MockitoExtension:
In this method for every super class the Test class has(
classContext.getSuperclass()
) it triggers the injection(injectCloseableMocks
) yet another time(while (classContext != Object.class)
). That might be clever if the super class has@InjectMocks
annotated instances its-self. But it is odd to callcloseables.add(injectCloseableMocks(testInstance));
instead ofcloseables.add(injectCloseableMocks(classContext));
then. For me it would make more sense to call it on the super class then instead of repeating the initialization yet another time. But I have not tested this change. I can not foresee what implications this would have. Fixing this concrete bug is probably better of in the hands of somebody that is more familiar with Mockito's internal workings.Why even care about this bug
Seems like it still initializes the instance right? Who does
anyways?
Since newer Java versions get stricter in enforcing reflections over module borders this will increasingly become an issue. In my case Mockito does access a field via reflections it is not allowed to making it fail. But it should never have tried field injection since the class had a working constructor. Due to the bug that is descripted here it did nevertheless.
Versions:
JDK 17, Windows 11, Mockito Version 4.4.0 (Even though this all should be reproducible elsewhere and with different versions)
Thank you for working on Mockito. Great Framework ❤️
Any more questions? I am happy to help.
The text was updated successfully, but these errors were encountered: