This post originated from an RSS feed registered with Java Buzz
by Jon Tirsen.
Original Post: Testing advice/interceptors
Feed Title: jutopia
Feed URL: http://sedoparking.com/search/registrar.php?domain=®istrar=sedopark
Feed Description: Ramblings on Java, AOP, TDD, agile and so on. some like it jutopic!
Notice how I mock the target of the call with an anonymous class. The target informs the surrounding code wheather it was called through the boolean wasCalled. If the interceptor was to set up some specific state during the call the asserts would go inside the anonymous class, in that case the wasCalled is used just to ensure that target was invoked at all.
There are lots of different ways of doing the above, depending on the situation. In this simple case I prefer the anonymous class due to it's simplicity (KISS). If there's more complex interactions you could use a simple String that you append each call to, a simple assert is used to check wheather calls where made in the correct order. You could also use dynamic mocks or some other variety, but don't get carried away with the fancy stuff, keep it simple. You might use an external class too, but that makes it a bit more awkward to use and a lot harder to read. You want to be able to grasp the entire test from looking at one place only. In most cases I prefer anonymous or inner classes over external classes for this reason. The CacheTestCalculations interface should have been inner but unfortunately the current version of QDox does not support inner-classes (so I can't use attributes on it).
The advice is tested in isolation from any other parts of your system. This is what makes it a unit-test. This makes the test easy to understand and stable in the sense that it won't break for reasons that are outside itself. If the test breaks you know exactly where to look: in the advice you're testing.
Test the advice extensively. Since the advice isn't tested together with other advice that it might interfere with it's important that the advice inner workings is defined in highest possible detail. A coverage tool is useful but doesn't quite do the trick here because a simple invocation will cover all of the tests, and that does not imply it's been properly tested (you need the asserts too, you know).
In Nanning (just as in Aspect/J) several advice and introductions can be modularized into a single "aspect". It might make sense to write unit-tests for the aspect as a whole too. Any interference between several advice should be local to a single aspect only. Aspects should be orthogonal with respect to each other. This might not always be possible and in that case you might have to write tests that test the interference. Non-local interference should be minimized of course, as it makes the system a nightmare to maintain.
I'm not sure this testing strategy is usable for "non-fluid" AOP frameworks. That is AOP frameworks where aspects can't be dynamically deployed and undeployed. A well-known example of a non-fluid AOP framework is Aspect/J, to use this strategy in Aspect/J you would probably need to recompile between each unit-test. Not acceptable. Nanning is fluid of course. :-)