andresp
andresp

Reputation: 1654

Unit testing classes weaved by aspects with dependency injected Spring beans

Because Spring-driven AspectJ LTW is unable to weave classes loaded before Spring initializes, I'm converting a Spring project to pure Aspectj LTW (with the AspectJ weaver java agent).

However, this collaterally enables AOP in our Unit Tests because I need to add AspectJ's agent to the Maven Surefire plugin argLine parameter and to the default TestNG configuration in my team's IDE (IntelliJ IDEA).

This wouldn't be a problem if our Aspects were not dependent on Spring, but some of them actually require Spring beans to be injected in their fields via @Resource notation. As Spring is not started during Unit Tests, that fields will be null and result in NullPointerExceptions.

Even though I could configure two independent executions of the Surefire plugin during the build process: one with the agent and the other without it; this solution would become impractical as each developer would still need to change the IDE's test configuration for unit tests vs other tests that actually require AOP and start Spring, for every independent test execution (i.e. outside of Maven build process).

What would be the best approach in order to solve this problem? Is there any way to disable AspectJ's LTW while still keeping the java agent configuration intact? Or maybe another and more flexible way to configure AspectJ's LTW that doesn't have the problems of Spring-driven AspectJ LTW?

Upvotes: 2

Views: 1941

Answers (1)

kriegaex
kriegaex

Reputation: 67437

We could argue about whether or not it is a good design decision to make pure AspectJ aspects dependent on Spring magic or if it would be an option to inject mock-ups for your Spring beans into the aspects. Actually a unit test is not a unit test if it requires such a framework. The same is arguably true for aspects.

Anyway, here is a cheap solution: Use if() pointcuts for all relevant advice and make them dependent on whether you are in testing mode or not. The performance overhead will usually be minimal, don't worry. Just give it a try before you say it is too expensive.

Also possible, but more expensive would be to determine if a test class is in the control flow of the currently intercepted joinpoint via cflow() or cflowbelow(). I do not recommend it in this case though.

A third option might be to add another META-INF/aop.xml to the classpath in testing mode which contains a global exclude statement excluding all classes from weaving. The AspectJ documentation explains how multiple aop.xml are merged logically. Probably this option is the best because it does not require you to change your aspects. I have not tried, but I would if you encounter any difficulties and give me a sign.

Upvotes: 3

Related Questions