2

I am using the aspectj maven plugin to weave Aspects at compile time. When I run the application, the class with the @Advice annotation is being instantiated just before the first time the advice is called. For example:

@Aspect
public class MyAdviceClass {

    public MyAdviceClass() {
        System.out.println("creating MyAdviceClass");
    }

    @Around("execution(* *(..)) && @annotation(timed)")
    public Object doBasicProfiling(ProceedingJoinPoint pjp, Timed timed) throws Throwable {
        System.out.println("timed annotation called");
        return pjp.proceed();
    }
}

If I have a method using the @Timed annotation, the "creating MyAdviceClass" will be printed the first time that method is called and "timed annotation called" will be printed every time.

I would like to unit test the functionality of the advice by mocking some components in MyAdviceClass however can not do this because MyAdviceClass is instantiated by AspectJ just in time, not through Spring Beans.

What is the best practice method for unit testing something like this?

tgrosinger
  • 2,463
  • 2
  • 30
  • 38
  • Usually unit tests involve mocking external dependencies, but I don't see any. I'm guessing you want to mock some external dependencies? Or do you want to mock the advice? – Taylor Feb 12 '14 at 19:18
  • Create a `MyAdviceClass` instance using its constructor, use mocks for `ProceedingJoinPoint` and `Timed`? –  Feb 12 '14 at 19:59
  • @Taylor, I have excluded them for simplicity. @RC., you are correct that I could test the `doBasicProfiling` method that way, but I would also like to test that the advice is called when I execute an annotated method. – tgrosinger Feb 12 '14 at 23:37
  • The easiest way is to mock an externality of the aspect (some service/dao/whatever it calls), inject that, and verify that it's called as you expect – Taylor Feb 13 '14 at 04:06
  • Yes, exactly. Unfortunately since AspectJ is creating the instance the first time it is needed rather than through a Spring Bean, I can not find the instance that it creates in order to do that injection. – tgrosinger Feb 13 '14 at 15:45

1 Answers1

0

I have found the solution and would like to post it for anyone else that encounters this. The trick is using factory-method="aspectOf" in your spring bean definition. So using the example above, I would add this line to my applicationContext.xml

<bean class="com.my.package.MyAdviceClass" factory-method="aspectOf"/>

Any my unit test would look something like this:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:/META-INF/spring/applicationContext.xml")
public class MyAdviceClassTest {
    @Autowired private MyAdviceClass advice;
    @Mock private MyExternalResource resource;

    @Before
    public void setUp() throws Exception {
        initMocks(this);
        advice.setResource(resource);
    }

    @Test
    public void featureTest() {
        // Perform testing
    }
}

More details are available here.

tgrosinger
  • 2,463
  • 2
  • 30
  • 38