Blog

Testing with(out) aspects

26 Sep, 2007

Recently I wanted to add an aspect to some domain object, so that it was saved, the moment it changed state. However, after adding this aspect, the whole build of course failed, because a lot of the unit tests weren’t expecting the calls which were now woven into the domain object.

Of course I could alter all the unit tests so that they reflected the change in code. But this seems to defy the whole purpose of the aspect. Another option is of course postponing the weaving until after the unit tests. This didn’t seem to be the correct solution either, because I did want to write at least some tests to test that the aspect was doing its job.
So how did I get around this? The answer lay in Spring AOP. It shares much of the syntax of AspectJ, though it doesn’t (yet) offer all of the functionality. I’ve created a small sample for your reading pleasure.
At first we’ll look at a simple domain without aspects.
Victim.java

public class Victim {
    private int itemsInPosession;
    public Victim(int itemsInPosession) {
        this.itemsInPosession = itemsInPosession;
    }
    public void itemStolen() {
        if (itemsInPosession > 0) {
            itemsInPosession--;
        }
    }
    public int getItemsInPosession() {
        return itemsInPosession;
    }
}

And Thief.java

public class Thief {
    public void steal(Victim victim) {
        victim.itemStolen();
    }
}

Of course we have a unit test to test that the Thief indeed steals from a Victim:

public class ThievingTest extends TestCase {
    public void testShouldSucceedInStealing() {
        Victim victim = new Victim(1000);
        Thief thief = new Thief();
        thief.steal(victim);
        assertEquals(999, victim.getItemsInPosession());
    }
}

Running this test shows that the Thief succeeds in stealing an item from the Victim. But of course, we can’t let Thiefs randomly steal from Victims. We need to introduce a PoliceAgent who can put the Thief in Jail. Let’s add an interface to Jail, and write a new test asserting that the Thief is put in Jail for stealing.
Jail.java

public interface Jail {
    public void putInJail(Thief thief);
}

And the second unit test class:

public class ThievingJailTest extends TestCase {
    private Jail jail;
    public void setUp() {
        jail = EasyMock.createMock(Jail.class);
    }
    public void testShouldLandInJailForStealing() {
        Victim victim = new Victim(1000);
        Thief thief = new Thief();
        jail.putInJail(thief);
        EasyMock.replay(jail);
        thief.steal(victim);
        EasyMock.verify(jail);
        assertEquals(1000, victim.getItemsInPossession());
    }
}

Of course, this test fails, because there is no way to put the Thief in Jail yet. Let’s add the PoliceAgent as an aspect, because we want to intercept the Thief that is stealing:
PoliceAgentAspect.java

@Aspect
public class PoliceAgentAspect {
    private Jail jail;
    @Around("execution(void *Thief.steal(..))")
    public void arrestThief(ProceedingJoinPoint joinPoint) {
        Thief thief = (Thief) joinPoint.getThis();
        jail.putInJail(thief);
    }
    public void setJail(Jail jail) {
        this.jail = jail;
    }
}

In order for Spring AOP to notice the aspect, and do the wiring, we need to add an applicationContext.xml with the correct beans. In this simple example, the following suffices (I’ve stripped the namespace declarations for readability):


	
		
	
	
		
	
	
	

You can see that in the Spring context I’ve mocked the Jail. This code previously was in the unit test. Also I’ve added the Thief as a bean which can be injected. This ensures that Spring can intercept the Thief with the PoliceAgentAspect. The modified test looks like this:

public class ThievingJailTest extends AbstractDependencyInjectionSpringContextTests {
    private Jail jail;
    private Thief thief;
    @Override
    protected String[] getConfigLocations() {
        setAutowireMode(AUTOWIRE_BY_NAME);
        return new String[] {"applicationContext.xml"};
    }
    public void testShouldBePutInJailByPoliceAgentAspect() {
        Victim v = new Victim(1000);
        jail.putInJail(thief);
        EasyMock.replay(jail);
        thief.steal(v);
        EasyMock.verify(jail);
        assertEquals(1000, v.getItemsInPosession());
    }
    public void setJail(Jail jail) { this.jail = jail; }
    public void setThief(Thief thief) { this.thief = thief; }

When we now run this test it succeeds. Also our old test still runs perfectly and provides a green light. Using Spring AOP, we can ensure that our code works when not using aspects, and that the adding of aspects to our code gives the intended results.

guest
5 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Vikas Hazrati
Vikas Hazrati
14 years ago

Good post Jeroen! It is amazing how you think about testing!
I was wondering something after reading the post, on what are the
scenarios in which we would like to use aspects.
Since you mentioned that \”wanted to add an aspect to some domain object,
so that it was saved, the moment it changed state\”, i was wondering that
is this the best case?
Since aspects are meant for addressing cross cutting functionality and
what you mention seems to be more like a business requirement, this made
me think that would aspect be good in such cases or would it be better
using something like an observer pattern in such a case.
Of course when you are changing the domain object in the Session when
you are using Hibernate you would get the domain object saved without
doing anything on your end but I am not sure how Hibernate does this for
you, whether it uses aspects or not.
What are your thoughts?
Jeroens Reply –>
Hi Vikas,
In this case I wasn\’t using Hibernate, so I needed a manual dao call
somewhere. So although you are right, that Hibernate would solve the
trick, it would come down to the same, as hibernate also uses a
generated proxy 😉
An observer pattern would also be a good solution. But then again, I
didn\’t want to fire off events from my domain object to let it know its
observers it changed state. I wanted to transparently add support for
saving the object to the database. That is why I also didn\’t want to
adapt the unit tests. The real scenario involved about 20 unit tests
which would all fail, because the domain object wasn\’t mocked 😉
I think that in this case aspects are very useful, because they can
transparently add support for this business requirement. You are of
course free to post a counter blog with an example with the observer
pattern 😀 I would be very curious to what your solution would be.
Regards,
Jeroen
Vikas\’ Reply –>
The reason why i was asking the question was that initially when we
start looking into aspects it is all about cross cutting concerns and
then suddenly we start putting aspects into our business scenarios too
and of course they make the code look cleaner and the domain is cleaner
as in your case. This led me to wonder that is there is another way
apart from aspects to do this. May be observer is not the perfect way
since you would be polluting the domain and the domain object should not
know how it is stored….hmmmm….but can it know when it should be
stored? What do you think?

Lars Vonk
14 years ago

hello, First of all very interesting post Jeroen. I had the same problem but just tested the java class together with the aspect. This is a better solution.
And in reply to what Vikas is wondering about: When Adrian Coyler was at Xebia last year he was talking about AOP and how AOP is all about modularity. Like Jeroen said AOP gives you the ability to implement one requirement in one place (or module). With OO languages this is not always possible, or it is possible but leads to ugly solutions. One of the most famous / ugliest ones is probably EJB1 and EJB2 where you needed to extend some class (what was that name again.. :-)). I think we should be more open in using aspects for implementing requirements just as if you were implementing it in a Java class. I think using Spring AOP makes it easier to adopt AOP because it basically will create the aspects under the cover for you.

hpl
hpl
14 years ago

The solution is like triggers to a database. Why not use a simple repository (interface in the domain) and call YYYYRepository.save(Object o)?
You accomplish the same only without the rather complex looking AOP configuration.

hpl
hpl
14 years ago

Hi Jeroen,
I agree, objects shouldn’t care about persistence. I just wanted to point out that a repository also decouples from a persistence mechanism. I’m not familiar with the problem but I’m sure there is a valid place where the save method can be called. Maybe a service? Maybe same place where you retrieve the object?

Explore related posts