- Chapter 6: 测试AOP
- 例子1:测试FooService的行为
- 例子2:测试FooAspect的行为
- 例子3:Spring Boot的例子
- 参考文档
Chapter 6: 测试AOP
Spring提供了一套AOP工具,但是当你把各种Aspect写完之后,如何确定这些Aspect都正确的应用到目标Bean上了呢?本章将举例说明如何对Spring AOP做测试。
首先先来看我们事先定义的Bean以及Aspect。
FooServiceImpl:
@Componentpublic class FooServiceImpl implements FooService {private int count;@Overridepublic int incrementAndGet() {count++;return count;}}
FooAspect:
@Component@Aspectpublic class FooAspect {@Pointcut("execution(* me.chanjar.aop.service.FooServiceImpl.incrementAndGet())")public void pointcut() {}@Around("pointcut()")public int changeIncrementAndGet(ProceedingJoinPoint pjp) {return 0;}}
可以看到FooAspect会修改FooServiceImpl.incrementAndGet方法的返回值,使其返回0。
例子1:测试FooService的行为
最简单的测试方法就是直接调用FooServiceImpl.incrementAndGet,看看它是否使用返回0。
SpringAop_1_Test:
@ContextConfiguration(classes = { SpringAopTest.class, AopConfig.class })public class SpringAop_1_Test extends AbstractTestNGSpringContextTests {@Autowiredprivate FooService fooService;@Testpublic void testFooService() {assertNotEquals(fooService.getClass(), FooServiceImpl.class);assertTrue(AopUtils.isAopProxy(fooService));assertTrue(AopUtils.isCglibProxy(fooService));assertEquals(AopProxyUtils.ultimateTargetClass(fooService), FooServiceImpl.class);assertEquals(AopTestUtils.getTargetObject(fooService).getClass(), FooServiceImpl.class);assertEquals(AopTestUtils.getUltimateTargetObject(fooService).getClass(), FooServiceImpl.class);assertEquals(fooService.incrementAndGet(), 0);assertEquals(fooService.incrementAndGet(), 0);}}
先看这段代码:
assertNotEquals(fooService.getClass(), FooServiceImpl.class);assertTrue(AopUtils.isAopProxy(fooService));assertTrue(AopUtils.isCglibProxy(fooService));assertEquals(AopProxyUtils.ultimateTargetClass(fooService), FooServiceImpl.class);assertEquals(AopTestUtils.getTargetObject(fooService).getClass(), FooServiceImpl.class);assertEquals(AopTestUtils.getUltimateTargetObject(fooService).getClass(), FooServiceImpl.class);
这些是利用Spring提供的AopUtils、AopTestUtils和AopProxyUtils来判断FooServiceImpl Bean是否被代理了(Spring AOP的实现是通过动态代理来做的)。
但是证明FooServiceImpl Bean被代理并不意味着FooAspect生效了(假设此时有多个@Aspect),那么我们还需要验证FooServiceImpl.incrementAndGet的行为:
assertEquals(fooService.incrementAndGet(), 0);assertEquals(fooService.incrementAndGet(), 0);
例子2:测试FooAspect的行为
但是总有一些时候我们是无法通过例子1的方法来测试Bean是否被正确的advised的:
- advised方法没有返回值
- Aspect不会修改advised方法的返回值(比如:做日志)
那么这个时候怎么测试呢?此时我们就需要用到Mockito的Spy方法结合Spring Testing工具来测试。
SpringAop_2_Test:
@ContextConfiguration(classes = { SpringAop_2_Test.class, AopConfig.class })@TestExecutionListeners(listeners = MockitoTestExecutionListener.class)public class SpringAop_2_Test extends AbstractTestNGSpringContextTests {@SpyBeanprivate FooAspect fooAspect;@Autowiredprivate FooService fooService;@Testpublic void testFooService() {// ...verify(fooAspect, times(2)).changeIncrementAndGet(any());}}
这段代码和例子1有三点区别:
- 启用了
MockitoTestExecutionListener,这样能够开启Mockito的支持(回顾一下Chapter 3: 使用Mockito) @SpyBean private FooAspect fooAspect,这样能够声明一个被Mockito.spy过的Beanverify(fooAspect, times(2)).changeIncrementAndGet(any()),使用Mockito测试FooAspect.changeIncrementAndGet是否被调用了两次
上面的测试代码测试的是FooAspect的行为,而不是FooServiceImpl的行为,这种测试方法更为通用。
例子3:Spring Boot的例子
上面两个例子使用的是Spring Testing工具,下面举例Spring Boot Testing工具如何测AOP(其实大同小异):
SpringBootAopTest:
@SpringBootTest(classes = { SpringBootAopTest.class, AopConfig.class })@TestExecutionListeners(listeners = MockitoTestExecutionListener.class)public class SpringBootAopTest extends AbstractTestNGSpringContextTests {@SpyBeanprivate FooAspect fooAspect;@Autowiredprivate FooService fooService;@Testpublic void testFooService() {// ...verify(fooAspect, times(2)).changeIncrementAndGet(any());}}
参考文档
- Aspect Oriented Programming with Spring
- AopUtils
- AopTestUtils
- AopProxyUtils
- spring源码EnableAspectJAutoProxyTests
- spring源码AbstractAspectJAdvisorFactoryTests
