Integration tests for AspectJ
Asked Answered
S

2

7

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

@Aspect
@Component
public class SampleAspect {

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

   private int count;

   public int getCount(){
      return count;
   }

    public void setCount(){
      this.count= count;
    }


    @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");
         setCount(1);
         return point.proceed();

    }

}

So the above aspect intercepts whenever jointpoint matches the pointcut. Its working fine. But my question is how to perform Integration test.

What I have done is I created the attribute "count" in Aspect for tracking and asserted it in my Junit. I am not sure if this is good or is there a better way of doing integration testing on aspects.

Here is the snippet of Junit what I have done. I presented in bad way but I hope its undestandable of what I have done for Integration testing.

@Test
public void testSamepleAspect(){
   sampleAspect.intercept(mockJointPoint);
   Assert.assertEquals(simpleAspect.getCount(),1);
}
Seifert answered 3/1, 2017 at 15:16 Comment(3)
@kriegaex could you help me with this...Seifert
I think you should to define count variable as static.Arbalest
I am a bit busy at the moment, but I will answer later, @karthik. Just quickly: The sample aspect has a singleton instatiation model, so it is a rather philosophical question whether the member should be static or not. But no matter what, it is not a good idea to do that because it inflicts a runtime penalty on the aspect, especially when applied broadly. BTW, it is not thread-safe either. Furthermore you should not pollute your production code with testing stuff. I am going to present a (hopefully better) solution later. :-)Justitia
J
4

Let us use the same sample code as in my answer to the related AspectJ unit testing question:

Java class to be targeted by aspect:

package de.scrum_master.app;

public class Application {
    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();
    }
}

You have several options, depending on what exactly you want to test:

  1. You can run the AspectJ compiler and verify its console output (with weaving info enabled) in order to make sure that the expected joinpoints are actually woven and others are not. But this would rather be a test for your AspectJ configuration and the build process as such than a real integration test.
  2. Similarly, you can create a new weaving classloader, load the aspect and then a few classes (load-time weaving, LTW) in order to dynamically check what gets woven and what does not. In this case you are testing if your pointcuts are correct rather than the integrated application consisting of core + aspect code.
  3. Last, but not least, you can perform a normal integration test, assuming how the application should behave after core + aspect code have been woven correctly. How to do this, depends on you concrete situation, specifically on what kind of side effect your aspect adds to the core code.

Subsequently I will describe option no. 3. Looking at the sample code above, we see the following side effects:

  • For small positive numbers, the aspect passes through the original parameter value to the intercepted method, the only side effect being additional log output.
  • For negative numbers, the aspect passes through the negated parameter value (e.g. turning -22 into 22) to the intercepted method, which is nicely testable.
  • For larger positive numbers, the aspect throws an exception, effectively stopping the original method from being executed at all.

Integration test for aspect:

package de.scrum_master.aspect;

import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.matches;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import java.io.PrintStream;

import org.junit.*;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

import de.scrum_master.app.Application;

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

    private Application application = new Application();

    private PrintStream originalSystemOut;
    @Mock private PrintStream fakeSystemOut;

    @Before
    public void setUp() throws Exception {
        originalSystemOut = System.out;
        System.setOut(fakeSystemOut);
    }

    @After
    public void tearDown() throws Exception {
        System.setOut(originalSystemOut);
    }

    @Test
    public void testPositiveSmallNumber() throws Throwable {
        application.doSomething(11);
        verify(System.out, times(1)).println(matches("execution.*doSomething.* 11"));
        verify(System.out, times(1)).println(matches("Doing something with number 11"));
    }

    @Test
    public void testNegativeNumber() throws Throwable {
        application.doSomething(-22);
        verify(System.out, times(1)).println(matches("execution.*doSomething.* -22"));
        verify(System.out, times(1)).println(matches("Doing something with number 22"));
    }

    @Test(expected = RuntimeException.class)
    public void testPositiveLargeNumber() throws Throwable {
        try {
            application.doSomething(333);
        }
        catch (Exception e) {
            verify(System.out, times(1)).println(matches("execution.*doSomething.* 333"));
            verify(System.out, times(0)).println(matches("Doing something with number"));
            assertEquals("oops", e.getMessage());
            throw e;
        }
    }
}

Et voilà, we are testing exactly the three types of side effects our sample aspect has by inspecting log output to a mock instance of System.out and by making sure that the expected exception is thrown for larger positive numbers.

Justitia answered 3/1, 2017 at 22:6 Comment(2)
if my aspect don't call System.out.println ? Do you suggest to add a line to print something and check in the IT as you did? Are there any other way to verify it? Mockito.verify want a mock as 1st param, but if I mock the joinpoint and not the aspect, the mockedJoinPoint is never called, if I mock the aspect the real aspect will not fire...Wald
I am not sure I understand the question. Your test verifies whatever result the aspect-enhanced code under test yields or whatever other side-effect the aspect is supposed to cause. Verifying logging is one of the trickiest things to verify, most other effects of AOP should be much easier to test. There is not much difference to testing other types of Java code, the principles of testing remain the same. If you want a more detailed answer, please ask a new question and provide a complete MCVE.Justitia
H
-2

@kriegaex What should be the test case implementation for below code

@Aspect
@Component
@Slf4j
public class SampleAspect {

    @Value("${timeout:10}")
    private long timeout;

    @Around("@annotation(com.packagename.TrackExecutionTime)")
    public Object intercept( ProceedingJoinPoint point) throws Throwable {

        long startTime = System.currentTimeMillis();

        Object obj = point.proceed();

        long endTime  = System.currentTimeMillis();

        long timeOut = endTime-startTime;

        if(timeOut > timeout) 
         {
          log.error("Error occured");
        }
          return obj;
    }
}

link:Junit-integration test for AOP Spring

Hanghangar answered 22/3, 2022 at 20:17 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Chukker

© 2022 - 2024 — McMap. All rights reserved.