• Chapter 6: 测试AOP
    • 例子1:测试FooService的行为
    • 例子2:测试FooAspect的行为
    • 例子3:Spring Boot的例子
    • 参考文档

    Chapter 6: 测试AOP

    Spring提供了一套AOP工具,但是当你把各种Aspect写完之后,如何确定这些Aspect都正确的应用到目标Bean上了呢?本章将举例说明如何对Spring AOP做测试。

    首先先来看我们事先定义的Bean以及Aspect。

    FooServiceImpl:

    1. @Component
    2. public class FooServiceImpl implements FooService {
    3. private int count;
    4. @Override
    5. public int incrementAndGet() {
    6. count++;
    7. return count;
    8. }
    9. }

    FooAspect:

    1. @Component
    2. @Aspect
    3. public class FooAspect {
    4. @Pointcut("execution(* me.chanjar.aop.service.FooServiceImpl.incrementAndGet())")
    5. public void pointcut() {
    6. }
    7. @Around("pointcut()")
    8. public int changeIncrementAndGet(ProceedingJoinPoint pjp) {
    9. return 0;
    10. }
    11. }

    可以看到FooAspect会修改FooServiceImpl.incrementAndGet方法的返回值,使其返回0。

    例子1:测试FooService的行为

    最简单的测试方法就是直接调用FooServiceImpl.incrementAndGet,看看它是否使用返回0。

    SpringAop_1_Test:

    1. @ContextConfiguration(classes = { SpringAopTest.class, AopConfig.class })
    2. public class SpringAop_1_Test extends AbstractTestNGSpringContextTests {
    3. @Autowired
    4. private FooService fooService;
    5. @Test
    6. public void testFooService() {
    7. assertNotEquals(fooService.getClass(), FooServiceImpl.class);
    8. assertTrue(AopUtils.isAopProxy(fooService));
    9. assertTrue(AopUtils.isCglibProxy(fooService));
    10. assertEquals(AopProxyUtils.ultimateTargetClass(fooService), FooServiceImpl.class);
    11. assertEquals(AopTestUtils.getTargetObject(fooService).getClass(), FooServiceImpl.class);
    12. assertEquals(AopTestUtils.getUltimateTargetObject(fooService).getClass(), FooServiceImpl.class);
    13. assertEquals(fooService.incrementAndGet(), 0);
    14. assertEquals(fooService.incrementAndGet(), 0);
    15. }
    16. }

    先看这段代码:

    1. assertNotEquals(fooService.getClass(), FooServiceImpl.class);
    2. assertTrue(AopUtils.isAopProxy(fooService));
    3. assertTrue(AopUtils.isCglibProxy(fooService));
    4. assertEquals(AopProxyUtils.ultimateTargetClass(fooService), FooServiceImpl.class);
    5. assertEquals(AopTestUtils.getTargetObject(fooService).getClass(), FooServiceImpl.class);
    6. assertEquals(AopTestUtils.getUltimateTargetObject(fooService).getClass(), FooServiceImpl.class);

    这些是利用Spring提供的AopUtils、AopTestUtils和AopProxyUtils来判断FooServiceImpl Bean是否被代理了(Spring AOP的实现是通过动态代理来做的)。

    但是证明FooServiceImpl Bean被代理并不意味着FooAspect生效了(假设此时有多个@Aspect),那么我们还需要验证FooServiceImpl.incrementAndGet的行为:

    1. assertEquals(fooService.incrementAndGet(), 0);
    2. assertEquals(fooService.incrementAndGet(), 0);

    例子2:测试FooAspect的行为

    但是总有一些时候我们是无法通过例子1的方法来测试Bean是否被正确的advised的:

    1. advised方法没有返回值
    2. Aspect不会修改advised方法的返回值(比如:做日志)

    那么这个时候怎么测试呢?此时我们就需要用到Mockito的Spy方法结合Spring Testing工具来测试。

    SpringAop_2_Test:

    1. @ContextConfiguration(classes = { SpringAop_2_Test.class, AopConfig.class })
    2. @TestExecutionListeners(listeners = MockitoTestExecutionListener.class)
    3. public class SpringAop_2_Test extends AbstractTestNGSpringContextTests {
    4. @SpyBean
    5. private FooAspect fooAspect;
    6. @Autowired
    7. private FooService fooService;
    8. @Test
    9. public void testFooService() {
    10. // ...
    11. verify(fooAspect, times(2)).changeIncrementAndGet(any());
    12. }
    13. }

    这段代码和例子1有三点区别:

    1. 启用了MockitoTestExecutionListener,这样能够开启Mockito的支持(回顾一下Chapter 3: 使用Mockito)
    2. @SpyBean private FooAspect fooAspect,这样能够声明一个被Mockito.spy过的Bean
    3. verify(fooAspect, times(2)).changeIncrementAndGet(any()),使用Mockito测试FooAspect.changeIncrementAndGet是否被调用了两次

    上面的测试代码测试的是FooAspect的行为,而不是FooServiceImpl的行为,这种测试方法更为通用。

    例子3:Spring Boot的例子

    上面两个例子使用的是Spring Testing工具,下面举例Spring Boot Testing工具如何测AOP(其实大同小异):

    SpringBootAopTest:

    1. @SpringBootTest(classes = { SpringBootAopTest.class, AopConfig.class })
    2. @TestExecutionListeners(listeners = MockitoTestExecutionListener.class)
    3. public class SpringBootAopTest extends AbstractTestNGSpringContextTests {
    4. @SpyBean
    5. private FooAspect fooAspect;
    6. @Autowired
    7. private FooService fooService;
    8. @Test
    9. public void testFooService() {
    10. // ...
    11. verify(fooAspect, times(2)).changeIncrementAndGet(any());
    12. }
    13. }

    参考文档

    • Aspect Oriented Programming with Spring
    • AopUtils
    • AopTestUtils
    • AopProxyUtils
    • spring源码EnableAspectJAutoProxyTests
    • spring源码AbstractAspectJAdvisorFactoryTests