JUnit tests for AspectJ

I am trying to write Junit tests for Custom Aspect. Here is a snippet of the Aspect class:

@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(); } } 

Thus, the aforementioned aspect is intercepted whenever the join point coincides with the pointcut. It works great.

But my question is how unit test this 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 intercepted by SampleAspect , since it matches pointcut. But how should I make sure in junits (possibly arguing) that my SampleAspect intercepts when this join point is called?

I cannot argue for the return value from intercept() , since it does nothing special except doing a joint point. Thus, my Junit cannot find the difference whether it is executed in aspectual or regular execution based on the returned values.

Any examples of code snippets when testing aspects would be great if provided. thanks

+17
source share
2 answers

I think you are trying to test this aspect weaving and pointcut mapping. Note that this will be an integration, not a unit test. If you really want the unit test of the logic of your aspect, and because in any case you noted the question "mockito", I suggest you do just that: Write a unit test and make fun of the connection point of the aspect and, possibly, its other parameters, if any. Here is a slightly more complex example with some intra-aspect logic:

The Java class should be aimed at the 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 the 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 goes to 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 make sure that the aspect does what it needs to and covers all the 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 to test the isolated logic aspect, not the wiring / weaving logic. For the latter, you will need a different type of test.

PS: For you, I used JUnit and Mockito. I usually just use Spock and its built-in mocking capabilities. ;-)

+17
source

@Dchris: The above answer from kriegaex gives a detailed explanation of how to simulate the weaving aspect, but if you plan to have some custom values ​​for neuria, maybe you can try like this

  ProceedingJoinPoint pjp; MethodSignature methodSig; @Before public void beforeEachTest() { // Use this to create all mocks annotated and inject it into utils MockitoAnnotations.initMocks(this); model = new LoggingModel(); model.setPhase("test phase"); model.setUser(null); model.setMethodName("customTestMethod"); model.setClassName("ServiceUtilsTest"); // mocking pjp pjp = Mockito.mock(ProceedingJoinPoint.class); methodSig = Mockito.mock(MethodSignature.class); } .... .... private Method testMethod() throws NoSuchMethodException { return getClass().getDeclaredMethod("customTestMethod"); } public void customTestMethod() { } 

Note. For this to work, you must remove the annotated @Mocks and use the manual mockery as described above. If you annotate it, you will get reasonable defaults

Hope this helps !!

0
source

Source: https://habr.com/ru/post/1013622/


All Articles