【问题标题】:JUnit tests for AspectJAspectJ 的 JUnit 测试
【发布时间】:2016-12-30 00:21:21
【问题描述】:

我正在尝试为自定义方面编写 Junit 测试。这是方面类代码段:

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

    }

}

因此,只要关节点与切入点匹配,上述方面就会进行拦截。它工作正常。

但我的问题是如何对该类进行单元测试。我有以下 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);

    }

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

我不能断言来自intercept() 的返回值,因为它除了执行关节点之外没有什么特别的。所以我的 Junit 无论是按方面执行还是基于返回值的常规执行都找不到任何区别。

如果提供任何有关方面测试的代码 sn-ps 示例将非常有用。谢谢

【问题讨论】:

    标签: java junit mockito aspectj spring-aop


    【解决方案1】:

    我认为您要测试的是方面编织和切入点匹配。请注意,这将是一个集成而不是单元测试。如果你真的想对你的切面逻辑进行单元测试,并且因为你已经用“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);
        }
    }
    

    待测方面:

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

    运行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)
    

    方面的单元测试:

    现在我们真的想验证切面是否做了它应该做的并覆盖了所有的执行路径:

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

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

    P.S.:我只为你使用了 JUnit 和 Mockito。通常我只使用 Spock 及其内置的模拟功能。 ;-)

    【讨论】:

    • 对于在这篇文章中寻找 Aspect 集成测试的任何人-我所做的是我在方面创建了一个属性来跟踪任何连接点行为,然后我在该属性上断言我的朱尼特。这对我有用。
    • 实际上你不应该为了启用集成测试而在一个方面添加成员或手动记账。它使方面的生产速度变慢。还有很多其他方法可以综合测试方面。也许这值得另一个问题。如果您创建一个,请不要通知我,我会尽力回答。
    • @kriegaex 。链接在这里。谢谢stackoverflow.com/questions/41446622/…
    • 我想模拟/测试 ProceedingJoinPoint 的值。我该怎么做?
    • 我上面的回答已经展示了如何模拟它。你能问得更准确一点吗?请不要劫持一个老问题,而是用一个真正的MCVE 创建一个新问题,即至少由一个方面、一个目标类和一个我可以编译、运行和分析的测试组成的东西?你试过什么,有什么问题?请不要在这里回答,如果标记为aspectj,我会找到新问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多