Issue
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);
}
Solution
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:
- 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.
- 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 rather testing if your pointcuts are correct than the integrated application consisting of core + aspect code.
- 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.
Answered By - kriegaex
Answer Checked By - Robin (JavaFixing Admin)