26

I am trying to write Junit tests for Custom Aspect. Here is the Aspect Class Snippet:

@Aspect
@Component
public class SampleAspect {

    private static Logger log = LoggerFactory.getLogger(SampleAspect.class);

    @Around("execution(* org.springframework.data.mongodb.core.MongoOperations.*(..)) || execution(* org.springframework.web.client.RestOperations.*(..))")
    public Object intercept(final ProceedingJoinPoint point) throws Throwable {
        logger.info("invoked Cutom aspect");
         return point.proceed();

    }

}

So the above aspect intercepts whenever jointpoint matches the pointcut. Its working fine.

But my question is how to unit test that class. I have the following Junit Test:

@Test(expected = MongoTimeoutException.class)
    public void TestWithMongoTemplate() {
        //MongoDocument class
        TestDocument test = new TestDocument();

        ApplicationContext ctx = new AnnotationConfigApplicationContext(TestMongoConfigurationMain.class);
        MongoTemplate mongoTemplate = ctx.getBean(MongoTemplate.class);

        //this call is being intercepted by SampleAspect
        mongoTemplate.save(test);

    }

So my mongoTemplate.save(test) in Junit is being intercepted by SampleAspect as it matches pointcut. But how should I make sure in junits(probably by asserting) that my SampleAspect is intercepting when that joint point is invoked?

I cannot assert on return value from intercept() as it does nothing special other than executing joint point. So my Junit cannot find any difference whether its being executed by aspect or a regular execution based on return values.

Any code snippets examples on aspect testing would be great if provided.Thanks

Maciej Kowalski
  • 25,605
  • 12
  • 54
  • 63
karthik
  • 773
  • 2
  • 11
  • 19

1 Answers1

28

I think what you are trying to test is aspect weaving and pointcut matching. Please note that that would be an integration rather than a unit test. If you really want to unit test your aspect logic and because you have tagged the question by "mockito" anyway, I suggest you do just that: Write a unit test and mock the aspect's joinpoint and maybe its other parameters, if any. Here is a slightly more complex example with some intra-aspect logic:

Java class to be targeted by aspect:

package de.scrum_master.app;

public class Application {
    public static void main(String[] args) {
        new Application().doSomething(11);
        new Application().doSomething(-22);
        new Application().doSomething(333);
    }

    public void doSomething(int number) {
        System.out.println("Doing something with number " + number);
    }
}

Aspect under test:

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class SampleAspect {
    @Around("execution(* doSomething(int)) && args(number)")
    public Object intercept(final ProceedingJoinPoint thisJoinPoint, int number) throws Throwable {
        System.out.println(thisJoinPoint + " -> " + number);
        if (number < 0)
            return thisJoinPoint.proceed(new Object[] { -number });
        if (number > 99)
            throw new RuntimeException("oops");
        return thisJoinPoint.proceed();
    }
}

Console log when running Application.main(..):

As you can see, the aspect passes on 11, negates -22 and throws an exception for 333:

execution(void de.scrum_master.app.Application.doSomething(int)) -> 11
Doing something with number 11
execution(void de.scrum_master.app.Application.doSomething(int)) -> -22
Doing something with number 22
execution(void de.scrum_master.app.Application.doSomething(int)) -> 333
Exception in thread "main" java.lang.RuntimeException: oops
    at de.scrum_master.aspect.SampleAspect.intercept(SampleAspect.aj:15)
    at de.scrum_master.app.Application.doSomething(Application.java:10)
    at de.scrum_master.app.Application.main(Application.java:7)

Unit test for aspect:

Now we really want to verify that the aspect does what it should and cover all execution paths:

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

import static org.mockito.Mockito.*;

public class SampleAspectTest {
    @Rule
    public MockitoRule mockitoRule = MockitoJUnit.rule();

    @Mock
    private ProceedingJoinPoint proceedingJoinPoint;

    private SampleAspect sampleAspect = new SampleAspect();

    @Test
    public void testPositiveSmallNumber() throws Throwable {
        sampleAspect.intercept(proceedingJoinPoint, 11);
        // 'proceed()' is called exactly once
        verify(proceedingJoinPoint, times(1)).proceed();
        // 'proceed(Object[])' is never called
        verify(proceedingJoinPoint, never()).proceed(null);
    }

    @Test
    public void testNegativeNumber() throws Throwable {
        sampleAspect.intercept(proceedingJoinPoint, -22);
        // 'proceed()' is never called
        verify(proceedingJoinPoint, never()).proceed();
        // 'proceed(Object[])' is called exactly once
        verify(proceedingJoinPoint, times(1)).proceed(new Object[] { 22 });
    }

    @Test(expected = RuntimeException.class)
    public void testPositiveLargeNumber() throws Throwable {
        sampleAspect.intercept(proceedingJoinPoint, 333);
    }
}

Now run this simple JUnit + Mockito test in order to test the aspect logic in isolation, not the wiring/weaving logic. For the latter you would need another type of test.

P.S.: Only for you I used JUnit and Mockito. Usually I just use Spock and its built-in mocking capabilities. ;-)

kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • 2
    For anyone who is looking for integration testing of Aspect in this post-What I had done is I created a attribute in the aspect that tracks any of the joinpoint behavior and then I have asserted on that attribute in my Junit. That worked for me. – karthik Jan 02 '17 at 23:57
  • 1
    Actually you should not add members or manual bookkeeping to an aspect just so as to enable integration testing. It makes the aspect slower in production. There are plenty other ways to integratively test aspects. Maybe this is worth another question. If you create one, feel free no notify me and I will try to answer. – kriegaex Jan 03 '17 at 10:11
  • 1
    @ kriegaex . Here is the link. Thanks http://stackoverflow.com/questions/41446622/integration-tests-for-aspectj – karthik Jan 03 '17 at 15:18
  • i want to mock/test the values of ProceedingJoinPoint. How can i do this? – Dchris Sep 27 '19 at 10:47
  • My answer above already shows how to mock it. Can you ask more precisely and please not hijack an old question but create a new one with a real [MCVE](https://stackoverflow.com/help/mcve), i.e. something consisting of at least an aspect, a target class and a test which I can compile, run and analyse? What have you tried, what is the problem? And please don't answer here, I will find the new question if tagged with _aspectj_. – kriegaex Sep 27 '19 at 10:58