All 2 entries tagged Spring
View all 45 entries tagged Spring on Warwick Blogs | View entries tagged Spring at Technorati | There are no images tagged Spring on this blog
February 13, 2008
Unit testing Spring AOP
This is an article for developers of Spring applications, who are looking at using its AOP support but not sure how to test it. If you’re not one of these people, go and read an interesting book instead. It also assumes you’re fairly proficient at Spring in general, but do leave a comment if something isn’t clear.
What is?
When we write Spring applications at work we like to have lots of unit tests, because they’re easy to do and make it red-flashing-light-obvious if something gets broken. Another cool thing you can do with Spring is Aspect Oriented Programming, allowing you to say things like “whenever an import() method is called anywhere in the code, do some logging”, without clogging up the actual import() method with logging code. This is great, but since AOP is a kind of magic voodoo on top of regular Java, you can’t just write a regular unit test and expect the advice to be applied. With logging you might sigh and continue with your work, but when you have AOP advice that checks something important like security permissions, you need to know that it’s actually going to do the check.
Explain again how sheep’s bladders may be employed to prevent earthquakes
Here’s an example applicationContext.xml with an advice bean that describes the aspect, and the autoproxy declaration that gets the advice applied (some package names may be fictional):
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd" default-autowire="byName"> <!-- this is what makes the magic happen -- > <aop:aspectj-autoproxy/> <!-- this is what describes what the magic is -- > <bean id="exceptionHandlingAspect" class="uk.ac.warwick.insanoflex.ExceptionHandlingAspect" /> <!-- all your other beans here -- > </beans>
The ExceptionHandlingAspect, which wants to send an email when an exception gets thrown in certain methods, looks a bit like this:
@Aspect public class ExceptionHandlingAspect { //pointcut matching all import() methods in our package space @Pointcut("execution(* uk.ac.warwick..*.import(..))") public void inGroupsImporter() {} @AfterThrowing(pointcut="inGroupsImporter()", throwing="ex") public void handleException(Exception ex) { sendEmailOrSomething() //whatever you want to do, really. } }
Actually testing
Enough guff, how to test it? The easiest (!) way is to use the Spring-provided AbstractSingleSpringContextTests, or one of its subclasses. This instantiates a whole ApplicationContext containing all your beans, including the autoproxying object, and that’s what wraps the relevant beans in an advice-laden proxy. You just have to specify the XML configuration file to load. It’s useful for general integration testing, making sure the individual parts are wired up correctly.
I’d recommend splitting up your config into smaller files and use import, then you can have a special test configuration file that just loads the stuff you’ll need to test, and uses in-memory data sources like HQLDB instead of a live database. Doing all this isn’t the topic of this article though, and there’s plenty of documentation for application context testing in general on the Spring website.
But how to check that the aspect is applied? Rather than test for its existence, you need to test for its effect. You need to do two things: make the import method throw an exception, and check that the emailer’s send method is called. Since you’re testing the aspect and not your importer or emailer, you can simply use pretend objects here, and put them in your test configuration file. As usual, things are much easier if your objects implement an interface so we’ll assume that’s happened here.
class MockImporter implements Importer { public void import() { throw new RuntimeException("oh dear me"); } } class MockEmailer implements Emailer { private boolean sent; public boolean isSent() { return sent; } public void sendMail() { sent = true; } }
Then we can test.
public class ExceptionMailerTest extends AbstractDependencyInjectionSpringContextTests { protected String[] getConfigLocations() { //your test spring config, containing definitions of our fake importer and mailer. return new String[] { "classpath:test-app-context.xml" }; } public void testExceptionIsMailed() { //grab the beans from the context MockEmailer emailer = (MockEmailer)getApplicationContext().getBean('emailer'); MockImporter importer = (MockImporter)getApplicationContext().getBean('importer'); assertFalse(emailer.isSent()); try { importer.import(); } catch (RuntimeException e) { //good; we still want the exception to get thrown } assertTrue(emailer.isSent()); } }
As long as the advice is being applied correctly, the mailer.send() method will have been called.
Improvements?
It’s a lot of effort to get a whole custom application context set up just to test one aspect (though you’re likely to reuse it to test other things). It would be good if it were possible to run Spring’s AOP proxying code more directly, though I think it depends on running inside an ApplicationContext; a manually-created context that didn’t use XML might possibly make things simpler, especially if it let us use JMock to make mock objects instead of implementing them manually. If I find a better method, I’ll report it here.
Eclipse AspectJ Plugin
Even if you’re not using true AspectJ compiling, it’s still really useful to have the AspectJ plugin for Eclipse (if you’re using Eclipse, that is). The annotations that Spring uses are actually just the AspectJ ones, so AspectJ can understand them just as well. It tells you straight away whether your advice is going to apply to the right things. The only thing to keep in mind is that true AspectJ can weave virtually anything, whereas Spring’s proxy AOP can only apply to public methods on Spring beans, so something that shows up here won’t necessarily work in Spring unless you are using the AspectJ weaver.
The plugin helped here as it found where my exception handler wasn’t being applied; I had Exception rather than RuntimeException, so it would only match methods which explicitly declared “throws Exception”.

September 03, 2007
Spring @Transactional handling with AspectJ
Save yourself and skip past this if you don’t know what Spring, AspectJ or transactions are :)
Your mileage may vary.
Here at Internet Towers we needed to have fine-grained control over where our Spring 2 application starts and ends database transactions. We use AOP to some extent already, but it just wraps around every handleRequest method on every edit-mode Controller. There’s built in support for annotations so that you can wrap a method in a transactional simply by annotating it with @Transactional and putting <tx:annotation-driven />
in the configuration file. Sounds pretty simple.
But this is achieved using Spring’s proxy objects (more on AOP proxies) wrapping around the original controller, which only work if the method to be transactional is public and called from outside (otherwise the object is calling itself and bypassing the proxy that’s wrapped around it). This isn't great because it means you can't even use it on a call to handleRequestInternal
. The solution is to use full-blown AspectJ weaving, which supports the same @Transactional annotation but can weave into any method and can be called from inside or out.
There are two ways to weave the transaction code in: load-time and compile-time. Load-time needs extra arguments when the VM is loaded which sounds like it would be a lot of hassle to remember to reconfigure every VM the application is deployed on. The easier we can drop the application file into a server’s deploy directory, the better. Compile time is nicer as it’s then built into the application, so I've gone ahead with that. The following is a rough guide to what's needed.
Firstly you need AspectJ, mainly the jar files that come with it, and also to make sure you have spring-aspects.jar that comes with Spring.
Sample application context:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop">
<aop:spring-configured>
<bean id="annotationTransactionAspect" factory-method="aspectOf"
class="org.springframework.transaction.aspectj.AnnotationTransactionAspect">
<property name="transactionManager" ref="transactionManager"></property>
</bean>
<!-- the rest of your application here -->
</beans>
Super. That's all the configuration the application needs to understand the @Transactional annotation. Now to add the weaving into the compile process. We use Ant to build, and there's an Ant task to do this. With the right options it will weave into your compiled classes. After some looking around I found that it's enough to just specify Spring's spring-aspects.jar.
Sample build.xml:
<target name="compileAndWeave">
<path id="web-src.compile.class.path">
<!-- paths to any libraries needed for compiling, including AspectJ -->
<path refid="external.libs.path" />
</path>
<!-- compile your source as normal, into some directory -->
<javac srcdir="src"
destdir="build/classes-preweave"
classpathref="web-src.compile.class.path" /><!-- load up the "iajc" task -->
<taskdef resource="org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties"
classpath="path/to/aspectj/aspectjtools.jar"/>
<!-- weave the just compiled classes from classes-preweave into classes -->
<iajc
inpath="build/classes-preweave"
destdir="build/classes"
classpathref="web-src.compile.class.path"
aspectpath="path/to/spring/spring-aspects.jar"
verbose="true" />
</target>
That was a bit long, but the first half of it is just the standard compiling stuff.
Anyway, drop your shiny packaged war file into Tomcat or whatever and it should just work, handling transactions according to the "transactionManager" bean wherever you add the @Transactional annotation. If you annotate a class it will work as if you annotated all the public methods in that class.
Good luck!