JUnit测试AspectJ

kar*_*hik 17 java junit aspectj spring-aop mockito

我正在尝试为Custom Aspect编写Junit测试.这是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();

    }

}
Run Code Online (Sandbox Code Playgroud)

因此,只要关节点与切入点匹配,上述方面就会截获.它的工作正常.

但我的问题是如何对该类进行单元测试.我有以下Junit测试:

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

    }
Run Code Online (Sandbox Code Playgroud)

因此,我mongoTemplate.save(test)在Junit中被拦截,SampleAspect因为它匹配切入点.但是,我应该如何确保junits(可能通过声明)SampleAspect在调用该关节点时我正在拦截?

我不能断言返回值,intercept()因为除了执行关节点之外没有什么特别之处.因此,我的Junit无法找到任何区别,无论是由方面执行还是基于返回值的常规执行.

如果提供方面测试的任何代码片段示例都会很棒.谢谢

kri*_*aex 18

我认为你要测试的是方面编织和切入点匹配.请注意,这将是一个集成而不是单元测试.如果你真的想要对你的方面逻辑进行单元测试,并且因为你已经用"mockito"标记了问题,我建议你这样做:写一个单元测试并模拟方面的连接点,也许还有其他参数,如果有的话.这是一个稍微复杂的示例,其中包含一些内部逻辑:

方面要定位的Java类:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

测试方面:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

运行时的控制台日志Application.main(..):

如你所见,方面传递11,否定-22并抛出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)
Run Code Online (Sandbox Code Playgroud)

方面的单元测试:

现在我们真的想要验证方面是否应该完成它并覆盖所有执行路径:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在运行这个简单的JUnit + Mockito测试,以便隔离测试方面逻辑,而不是布线/编织逻辑.对于后者,您需要另一种类型的测试.

PS:只有你我才使用JUnit和Mockito.通常我只使用Spock及其内置的模拟功能.;-)

  • 对于希望在本文中进行Aspect集成测试的任何人,我所做的就是在Aspect中创建一个属性,该属性跟踪任何连接点行为,然后在Junit中声明该属性。那对我有用。 (2认同)