Testing with(out) aspects

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.

Comments (5)

  1. Vikas Hazrati - Reply

    September 27, 2007 at 10:47 am

    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?

  2. Lars Vonk - Reply

    September 27, 2007 at 10:21 pm

    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.

  3. hpl - Reply

    September 27, 2007 at 11:11 pm

    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.

  4. Jeroen van Erp - Reply

    September 27, 2007 at 11:46 pm

    Hi hpl,
    You are right that any of my classes could simply call the DAO/Repository.
    But then again, I don't see whether they should care that the Object is persisted. In the real scenario, the domain object changes state in a processor which just receives the object as a parameter. It does not know where it comes from. So should the processor then be concerned with the fact that the object needs to be stored as it changes?

  5. hpl - Reply

    September 28, 2007 at 12:04 am

    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?

Add a Comment