Version 3

    Many people use mock frameworks in order to isolate their classes from other dependencies when unit testing. While these are powerful frameworks, inevitably one ends up writing either code or tests a certain way because the framework dictates it. Adapting code to better test it is, though pragmatic, not really desirable. AOP frameworks allow full flexibility to isolate pieces of code from other pieces of code, but not many people want to dive into that kind of complexity. What if we could get the power of AOP, with the simplicity of annotations and be able to write the body of our test methods free from all kinds of restrictions?

     

    Enter @Replace.

     

    Already present in the JBoss AOP 2.x series is a dynamic duo that allows us to do what we want (and with a much more powerful expression than using mock frameworks) - these two are the @Replace annotation and the MockAspect AOP interceptor (both in the org.jboss.aspects.mock package). From a test programmers view one only needs to understand the annotation:

     

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Replace {
        public String invocation();
        public String callbackClass();
        public String callbackMethod();
        public long expectedCalls() default Long.MAX_VALUE;
    }

     

    This annotation is used as a runtime construct (when running your unit tests) and it applies to methods (specifically: methods in your testcase). Let's use the sample test provided by the jboss-aop download that demonstrates its use (MockTestCase.java):

     

       @Replace(invocation = "org.jboss.test.aspects.mock.ExternalClass->calculate(int)", 
             callbackClass = "org.jboss.test.aspects.mock.MockTestCase", 
             callbackMethod = "callback")
       public void testBusinessServiceGetNumberExternalIsolated() throws Exception
       {
          BusinessService service = new BusinessService(666);
          assertEquals(1998, service.calculateExternal());
       }

     

    Here you can see how the annotation is used to indicate that:

     

    • When running this and only this test method:
      • Replace all calls to the calculate(int) method on the org.jboss.test.aspects.mock.ExternalClass class with the callback() method on the org.jboss.test.aspects.mock.MockTestCase class (actually this is the testcase class itself in this example)

     

    So, when the BusinessService being tested here (the complete example is in the jboss-aop distribution) actually calls ExternalClass.calculate, it is redirected to the callback method on the testcase. So no mock framework is necessary, you can dictate the outcome of the call just by implementing the method exactly as you wish.

     

    How does it work?

    This uses dynamic pointcut features in jboss-aop so that, in runtime, the annotation written in your test method actually creates bindings for jboss-aop which again causes jboss-aop to intercept invocations and redirect to your callback method. Pretty neat.

     

    Can I "mock" static/private/whatever code?

    Well, the "invocation" attribute on the annotation is really a jboss-aop pointcut, which gives you all the niceties and power of the underlying engine. That means that anything that can be pointcutted can be replaced.

     

    What about "expect" functionality?

    Well, since you can set the callback to a custom method, you have complete control over the returned values (if any) as well as doing checking on inputs to the isolated method.

     

    What about expected number of invocations?

    Actually, the annotation has support for this too. The attribute "expectedCalls" allows you to indicate a specific number of invocations, which will cause the test to fail if the actual number of invocations differ from this. This is an optional attribute.

     

    I don't like this compared to the powerful syntax of my chosen mock framework!

    Well, that's your opinion, and this is no silver bullet solution. However, many people want their tests to be as free from framework syntax as possible, many of them already offer annotation support moving in this direction.

     

    I would like to see this better documented/with more features/changed to suit my needs!

    It's. Open. Source. Contribute.

     

    See the main jboss-aop page for download information.