I'm currently writing JUnit unit-tests for a class in an Application that uses Spring and AspectJ. The class under test has a couple public methods that are advised by an around-advice method in an aspect class. The Aspect has a couple of injected fields that turn up null when the advised method is executed, even though I've successfully instantiated those beans in the test application context, and when their methods are called, they throw nullpointerexceptions. Here's a simplified version of the code:
The class to be tested:
public class ClassUnderTest {
@Inject
private Foo foo;
@Audit(StringValue="arg", booleanValue=true)
public Object advisedMethod() {
Object ret = new Object();
//things happen
return ret;
}
The Aspect:
@Aspect
@Configurable
public class AuditAspect implements Versionable {
@Inject
Foo foo;
@Inject
Bar bar;
@Around("@annotation(Audit)")
public Object aroundAdvice(ProceedingJoinPoint pjp, Audit audit) {
// Things happen
privMethod(arg);
// Yet other things happen
Object ret = pjp.proceed();
// Still more things happen
return ret;
}
private Object privMethod(Object arg) {
// Things continue to happen.
// Then this throws a NullPointerException because bar is null.
bar.publicBarMethod(arg2);
// Method continues ...
}
}
The Audit interface:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Audit {
String value() default "";
boolean bool() default false;
}
The Config file that provides the application context:
import static org.mockito.Mockito.*;
@Configuration
public class ClassUnderTestTestConfig {
@Bean
Foo foo() {
return mock(Foo.class);
}
@Bean
Bar bar() {
return mock(Bar.class);
}
@Bean
ClassUnderTest classUnderTest() {
return new ClassUnderTest();
}
@Bean
@DependsOn({"foo", "bar"})
Aspect aspect() {
return new Aspect();
}
}
The Test class:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextHierarchy({
@ContextConfiguration(classes = ClassUnderTestTestConfiguration.class),
@ContextConfiguration(classes = ClassUnderTest.class)
})
public class ClassUnderTestTest {
private static final String sessionNumber = "123456";
@Inject
Foo foo;
@Inject
Bar bar;
@Inject
ClassUnderTest classUnderTest;
@Inject
Aspect aspect;
@Test
public void test() {
// This call triggers the advice which ends up throwning
// a NullPointerException.
classUnderTest.advised();
}
}
I've also tried making my own Spring proxy and then adding the aspect to it manually as advised in this stack overflow post, by adding this code to the test class:
@Before
public void setUp() {
DataPointRestWebService target = new DataPointRestWebService();
AspectJProxyFactory proxyMaker = new AspectJProxyFactory(target);
proxyMaker.addAspect(auditAspect);
dataPointRestWebService = proxyMaker.getProxy();
}
However that ends up throwing:
AopConfigException: Advice must be declared inside an aspect type. Offending method 'public java.lang.Object Aspect.aroundAdvice' in class [Aspect]
I find this cryptic because I the Aspect class does have the @Aspect annotation before it, and the class works outside of a test environment.
I'm very new to Spring and AspectJ, so I'm completely open to the notion that I'm going about this all wrong. I've provided dummy code here in hopes of leaving out unhelpful specifics, but also because the working code is proprietary and not mine. If you think I've left out an important detail, let me know and I'll try to add it.
Thanks in advance for any help, and please let me know if I'm leaving out any crucial information.
EDIT:
By request, I've added the full NullPointerException stack trace:
java.lang.NullPointerException
at com.unifiedcontrol.aspect.AuditAspect.getCaller(AuditAspect.java:265)
at com.unifiedcontrol.aspect.AuditAspect.ajc$inlineAccessMethod$com_unifiedcontrol_aspect_AuditAspect$com_unifiedcontrol_aspect_AuditAspect$getCaller(AuditAspect.java:1)
at com.unifiedcontrol.aspect.AuditAspect.aroundAuditAdvice(AuditAspect.java:79)
at com.unifiedcontrol.server.rest.DataPointRestWebServiceTest.dummyTest(DataPointRestWebServiceTest.java:109)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:233)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:87)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:176)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Where AuditAspect == Aspect and DataPointRestWebService == ClassUnderTest.