Overview
We are starting to use Spring AOP and have some issues with existing tests using ReflectionTestUtils
.
In one test, a private method of spring-bean A
is invoked via ReflectionTestUtils.invokeMethod
. This method calls a method of an injected spring-bean B
.
With spring-aop
in use, this does not work anymore. CGlib will not provide a proxied version of the private method. Therefore the private method of the proxy is invoked, and the proxy does not have the DI-beans present and therefore we encounter a NullPointerException
.
Now, my original assumption was that ReflectionTestUtils
should handle this (as the setField
/getField
methods explicitly deal with Spring proxies).
My question is: Is my assumption wrong – and if so, how do we invoke private methods in test – or is this an omission in the ReflectionTestUtils
?
Form a user's perspective I would have hoped that ReflectionTestUtils
would at least handle the invocation of private methods (there should never be a reason to invoke private methods on a proxy, should there?).
I found issue #18622 which seems to be the point where AOP invocations in setField
/getField
were considered.
Related Issues
-
18622
Comment From: sbrannen
Hi @drachenpalme,
Thanks for raising the issue.
Now, my original assumption was that
ReflectionTestUtils
should handle this (as thesetField
/getField
methods explicitly deal with Spring proxies).My question is: Is my assumption wrong – and if so, how do we invoke private methods in test – or is this an omission in the
ReflectionTestUtils
?
ReflectionTestUtils
does not currently handle that scenario, simply because no one ever asked for such support before, and we've never needed it in our own tests.
The way to achieve that now is by invoking AopTestUtils.getUltimateTargetObject()
yourself and passing the result of that to ReflectionTestUtils
. For example:
ReflectionTestUtils.invokeGetterMethod(AopTestUtils.getUltimateTargetObject(bean), "privateProperty");
Form a user's perspective I would have hoped that
ReflectionTestUtils
would at least handle the invocation of private methods (there should never be a reason to invoke private methods on a proxy, should there?).
That's correct: there should never be a reason to invoke a private method (or, more specifically, a non-proxied method) on a CGLIB proxy.
I found issue #18622 which seems to be the point where AOP invocations in
setField
/getField
were considered.
That's correct. We'll do something similar for method invocations with ReflectionTestUtils
in Spring Framework 6.2, but we'll limit that to non-proxied methods invoked on a CGLIB proxy.
In the interim, please use AopTestUtils.getUltimateTargetObject()
manually as mentioned above.
Comment From: drachenpalme
Hi @sbrannen ,
thanks a lot for the quick reaction.
Should anyone else stumble upon this: As there are a lot of places in our code, which are affected by this, we choose to create a drop-in replacement for the ReflectionTestUtils
in a different namespace which anticipates your change - so we can simply exchange imports application-wide.
After some further digging: invokeGetterMethod
and invokeSetterMethod
do seem to have the same problem. We are not affected by this (as we do not use them), yet, consistency-wise, would it be a good idea to adapt them as well?
Comment From: sbrannen
thanks a lot for the quick reaction.
You're welcome.
Should anyone else stumble upon this: As there are a lot of places in our code, which are affected by this, we choose to create a drop-in replacement for the
ReflectionTestUtils
in a different namespace which anticipates your change - so we can simply exchange imports application-wide.
Sounds like a good plan.
After some further digging:
invokeGetterMethod
andinvokeSetterMethod
do seem to have the same problem. We are not affected by this (as we do not use them), yet, consistency-wise, would it be a good idea to adapt them as well?
Yes, I have already added the new support to the following locally.
org.springframework.test.util.ReflectionTestUtils.invokeSetterMethod(Object, String, Object, Class<?>)
org.springframework.test.util.ReflectionTestUtils.invokeGetterMethod(Object, String)
org.springframework.test.util.ReflectionTestUtils.invokeMethod(Object, Class<?>, String, Object...)
Comment From: sbrannen
This has been addressed in main
for inclusion in Spring Framework 6.2 RC1.
For details, see commit 38a56b3fdaade936d8660472122dc814773c89bd.