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提供的AopUtilsAopTestUtilsAopProxyUtils来判断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. }

参考文档