Method Interceptor not being called in Test - java

My method interceptor isn't called in test context. the test context is configured for my test class: with #ContextConfiguration(locations = {"classpath:testContext.xml"}) and I have a method interceptor:
public class MyMethodInterceptor implements MethodInterceptor {
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("invocation >>>> " + invocation);
}
and it's configured in my context.xml file like this:
<bean id="myMethodInterceptor" class="path.to.interceptor.MyMethodInterceptor"/>
<aop:config>
<aop:advisor pointcut="#within(path.to.annotation.MyAnnotation)"
advice-ref="myMethodInterceptor" order="10"/>
</aop:config>
my expectation is in my test when I call a method with MyAnnotation the interceptor is being called, but it will not. I'm using JUnit 4.12

when I call a method with MyAnnotation
This will never work, neither in a test nor in production code. The reason is simple: A method interceptor and use of the #within() pointcut designator are diametrically opposed to each other:
#within() intercepts everything (i.e. in Spring AOP method executions) inside annotated classes, while according to your description you want to intercept annotated methods.
The solution would be to either use #annotation(path.to.annotation.MyAnnotation) and annotate all target methods, or to continue using #within(path.to.annotation.MyAnnotation), but annotate target classes instead.

Related

Using #Retryable in methods define in spring bean's base class are not retried

I have a spring managed bean of type B. I have #EnableREtry in a #Configuration class. When I use #Retryable on doStuff(), the method gets retried on failure as expected.
But, the method I really want to retry is a method defined in the base class, A. A is a concrete class and not a spring managed bean. the doSomethingElse method doesn't get retried on throwing an exception.
I really want doSomethingElse to be retried, the base class method. However, I'm not sure how to do this. I'm guessing it's because A is a concrete class and not a bean, although it does serve as a base class.
Do I need to use a RetryableTemplate in class A?
public class B extends A {
public void doStuff() {
super.doSomethingElse();
}
}
public class A {
// doesn't actually retry
#Retryable
public void doSomething() {
throws new Exception();
}
}
#Retryable is implemented using Spring AOP.
Only external calls to retryable methods go through the proxy (which invokes the method within a RetryTemplate); internal calls within the class bypass the proxy and therefore are not retried.
You can play some tricks to get a reference to the proxy from the application context and call that, or simply use a RetryTemplate directly within your doStuff() method.

Spring pointcut for abstract class' protected method

I am using Spring 3.2 and AspectJ 1.7.1. (It is not likely that I can upgrade to later versions in the near future.)
I need to define a pointcut for a protected method in an abstract class. AFAIK I need AspectJ for methods that are not public, so I have only tried this with the (AspectJ) annotations:
package com.aspects;
#Aspect
public class Aspect{
#Before("execution(* com.x.y.x.MyClass.myMethod(..))")
public void beforeAspect(){
//do something here
}
}
In my beans.xml I have:
<aop:aspectj-autoproxy />
<bean id="myAspect" class="com.aspects.Aspect"/>
I have checked and my Aspect class is created (constructor is getting called), no exception is being thrown when the application is launched.
However I can not get the beforeAspect to be called. For public methods in non abstract classes this works. How can I make it work for protected method in abstract class?
You should add a + sign after the abstract class.
So
"execution(* com.x.y.x.MyClass.myMethod(..))"
should look like:
"execution(* com.x.y.x.MyClass+.myMethod(..))"
↑
The + is about inheritance either extending the given class (MyClass) or implementing an interface.
Actually your are using #AspectJ-style to configure Spring AOP. the <aop:aspectj-autoproxy> declares an AnnotationAwareAspectJAutoProxyCreator BeanPostProcessor but you need to run the aspectj compiler instead.
See 9.8 Using AspectJ with Spring applications in reference documentantion for more info about weaving aspects with Spring.
See Spring / #Transactional with AspectJ is totally ignored for more info about configure compile time weaving with maven.

How to autowire object to bean if class does not have setter

I have so controller
#Controller
public class MyController {
#Autowire
MyClass myClass;
//myClass doesn't have setter and getter
....
#RequestMapping("/path")
public String underTest(){
myClass.makeSomething();
return "html.jsp"
}
I want make mock test using Mockito and mock myClass.
In test class I want get myClass so:
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/BeanConfig.xml");
myClass = context.getBean("myClass ", MyClass .class);
But I need autowire this bean to Controller for testing controller's method(I think test code should not affect to normal code).
There are exist way to make it without writing of set method?
I want to check that myClass.makeSomething() invokes once in method underTest.
As long as your test for MyController resides in the same package as MyController itself (as it's usually done - same packages in different source folders), you can simply assign it:
MyController controller = new MyController();
controller.myClass = mockMyClass;
That's the reason not to put #Inject/#Autowired on private fields.
I'm not sure I agree with you that test code should not affect normal code. I think an entirely valid reason to refactor / rewrite production code is to make it more testable (this is probably achieved by making it more modular, which is generally a good thing anyway).
This is precisely why annotations like
#VisibleForTesting
exist. Then you can create a package-local setter for MyClass, add the above annotation (for information to other programmers and possibly code inspection tools) and set the field in your test (which should reside in the same package).
Alternatively, since you are using Mockito, you could simply annotate the MyController instance with #InjectMocks, eg
#Test
public class MyControllerTest {
#Mock
private MyClass mockMyClass;
#InjectMocks
private MyController myController;
#BeforeMethod
public void before() {
MockitoAnnotations.initMocks(this);
}
// do tests...
}
Note that #InjectMocks does not depend on any annotations on the target field (i.e. #Autowired, #Resource, #Inject etc). It just works. (Presumably you will still need those annotations for Spring injection, so don't remove them! The point is you can also use it for fields that aren't annotated).
Note also that, depending on which version of Mockito you are using, you may need to instantiate the MyController in the before() method before calling MockitoAnnotations.initMocks()
Try testing the controller directly with context.getBean(). MyClass will be autowired into it.
I agree with #axtavt's answer, however if you absolutely want to go your way with injecting the mock in an integration test, you can do this:
define a overriding bean configuration file, say bean-test-config.xml, with content along these lines:
<import resource="classpath:spring/BeanConfig.xml"/>
<bean name="myClass" factory-method="mock" class="org.mockito.Mockito">
<constructor-arg value="MyClass"></constructor-arg>
</bean>
This should correctly inject in a mock in your controller. You will have to get hold of this mock in your test and inject in any behavior that you are expecting from this mock though.

Spring AOP pointcut definition for string setter

I'm developing an aspect that checks string arguments of setter methods of my entity package for empty strings and replace them with null values. But unfortunately my aspect doesn't works well :(. I guess it is because of my pointcut definition, but I'm not sure.
My aspect looks like:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
#Aspect
public class EmptyStringToNullSetter {
private static final Logger LOGGER = LoggerFactory
.getLogger(EmptyStringToNullSetter.class);
public void check(final JoinPoint jp) {
LOGGER.debug(jp.getSignature().toLongString());
}
}
My spring config looks like:
<bean id="emptyStringToNullSetter" class="de.foo.util.aop.parameter.EmptyStringToNullSetter" />
<aop:config>
<aop:pointcut id="entityStringSetter" expression="execution(* de.foo.entity.*.set*(..)) and args(java.lang.String)" />
<aop:aspect id="checkEmptyStringsAspect" ref="emptyStringToNullSetter">
<aop:before method="check" pointcut-ref="entityStringSetter" />
</aop:aspect>
</aop:config>
My test class looks like:
import de.foo.entity.Period;
#ContextConfiguration(locations = { "/spring/test-util-context.xml" })
public class EmptyStringToNullSetterTest extends
AbstractJUnit4SpringContextTests {
#Test
public void testCheck() {
Period period = new Period();
period.setName("");
Assert.assertNull(period.getName());
}
}
When I execute my test the aspect doesn't intercept my setter. Do anyone has any idea why?!
Cheers,
Kevin
Since you are using proxy-based AOP, the advice will apply only to Spring beans and the "period" object isn't a bean. You need to either have "period" as a bean or use AspectJ's weaving based AOP. In either case, you will also need to use an around advice instead of before.
This design is very tricky and error-prone with Spring JDK proxy based AOP.
I've mentionned this point here: http://doanduyhai.wordpress.com/2011/08/08/spring-aop-advices-on-setters-not-trigged/
Basically, an aspect define with Spring AOP is implemented at runtime as a proxy object wrapping around the original target.
In the bean lifecycle, Spring will create proxies only after the bean is fully initialized, e.g. after all properties injection by setter.
So the first time your setter is called, it will not be intercepted by the advice because the proxy does not exist yet.
However all subsequent calls to the setter will be intercepted.
Furthermore, be careful about self-invocation issues, e.g. calling the setter() inside another target method.

Testing a Spring AOP Aspect

When writing aspects, how can I test that they do match and that they are invoked when I want them to?
I'm using #Aspect declarations with Spring 2.5.6.
I don't care about the functionality, that's extracted and tested otherwise.
There's three different things to test here:
Are your pointcuts matching what you expect?
Do your advice reference the right pointcut?
Does the advice do as you expect?
To test the pointcuts, you can define some test types that have the same package/type/method signatures as the intended "real" targets, then define a test advice against the pointcuts to ensure they are matched (also define some types that shouldn't be matched to ensure the pointcuts aren't too liberal). I usually do this by defining the advice to do a callback to a method in the test target, setting a flag, then assert the flag was set.
To test the advice is trickier. I tend to delegate all the advice body to a normal method, then focus on testing the method rather than the advice.
If you've done that, the only missing part is that your advice is applied to the correct pointcuts and actually calls the methods. If you're concerned this might be a problem you can do this by creating another aspect that matches your advice execution and sets a flag to show the expected delegated method was invoked by the aspect, and override the method to do nothing.
I ended up creating something which is a bit of an integration test, in the following way:
Created a Spring-aware JUnit test
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "aspects-test.xml" })
public class SomeAspectTest {
}
Created a spring configuration for this test which:
enables #AspectJ usage;
configures my aspect with dummy dependencies
declares a bean which should be picked up by the aspect
<aop:aspectj-autoproxy />
<bean class="SomeAspect">
<property name="userDetailsSource">
<bean class="StubUserDetailsSource"/>
</property>
</bean>
<bean class="DummyService"/>
In the unit test I retrieve the dummy service and invoke its methods
#Autowired
private DummyService _dummyService;
#Test(expected = ApplicationSecurityException.class)
public void adminOnlyFails() throws ApplicationSecurityException {
_dummyService.adminOnly();
}

Categories