Spring、Spring Boot和TestNG測試指南 - 測試AOP

Github地址html

Spring提供了一套AOP工具,可是當你把各類Aspect寫完以後,如何肯定這些Aspect都正確的應用到目標Bean上了呢?本章將舉例說明如何對Spring AOP作測試。java

首先先來看咱們事先定義的Bean以及Aspect。git

FooServiceImplgithub

@Component
public class FooServiceImpl implements FooService {

  private int count;

  @Override
  public int incrementAndGet() {
    count++;
    return count;
  }

}

FooAspectspring

@Component
@Aspect
public 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。segmentfault

例子1:測試FooService的行爲

最簡單的測試方法就是直接調用FooServiceImpl.incrementAndGet,看看它是否使用返回0。api

SpringAop_1_Testide

@ContextConfiguration(classes = { SpringAopTest.class, AopConfig.class })
public class SpringAop_1_Test extends AbstractTestNGSpringContextTests {

  @Autowired
  private FooService fooService;

  @Test
  public 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提供的AopUtilsAopTestUtilsAopProxyUtils來判斷FooServiceImpl Bean是否被代理了(Spring AOP的實現是經過動態代理來作的)。測試

可是證實FooServiceImpl Bean被代理並不意味着FooAspect生效了(假設此時有多個@Aspect),那麼咱們還須要驗證FooServiceImpl.incrementAndGet的行爲:

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

例子2:測試FooAspect的行爲

可是總有一些時候咱們是沒法經過例子1的方法來測試Bean是否被正確的advised的:

  1. advised方法沒有返回值

  2. 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 {

  @SpyBean
  private FooAspect fooAspect;

  @Autowired
  private FooService fooService;

  @Test
  public void testFooService() {

    // ...
    verify(fooAspect, times(2)).changeIncrementAndGet(any());

  }

}

這段代碼和例子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

@SpringBootTest(classes = { SpringBootAopTest.class, AopConfig.class })
@TestExecutionListeners(listeners = MockitoTestExecutionListener.class)
public class SpringBootAopTest extends AbstractTestNGSpringContextTests {

  @SpyBean
  private FooAspect fooAspect;

  @Autowired
  private FooService fooService;

  @Test
  public void testFooService() {

    // ...
    verify(fooAspect, times(2)).changeIncrementAndGet(any());

  }

}

參考文檔

相關文章
相關標籤/搜索