专栏首页颇忒脱的技术博客Spring、Spring Boot和TestNG测试指南 - 测试AOP

Spring、Spring Boot和TestNG测试指南 - 测试AOP

Github地址

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

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

FooServiceImpl

@Component
public class FooServiceImpl implements FooService {

  private int count;

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

}

FooAspect

@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。

例子1:测试FooService的行为

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

SpringAop_1_Test

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

  }

}

参考文档

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Spring、Spring Boot和TestNG测试指南 - @TestConfiguration

    @TestConfiguration是Spring Boot Test提供的一种工具,用它我们可以在一般的@Configuration之外补充测试专门用的Bea...

    颇忒脱
  • Spring、Spring Boot和TestNG测试指南 - 共享测试配置

    我们可以将测试配置放在一个@Configuration里,然后在测试@SpringBootTest或ContextConfiguration中引用它。

    颇忒脱
  • MySQL Master Slave Docker部署例子

    用Docker部署基于GTID的MySQL Master-Slave Replication例子。

    颇忒脱
  • CSS3有哪些好用的属性?

    1.前言 在月初的时候,发了CSS3热身实战--过渡与动画(实现炫酷下拉,手风琴,无缝滚动)。js的代码库也发过两次,两篇文章。之前也写了css3的热身实战,既...

    连胜
  • 羊皮书APP(Android版)开发系列(十九)在ScrollView中使用ListView、GridView的方法

    热心的程序员
  • Flutter进阶篇(5)-- 使用Flutter创建插件详解并发布到Pub库

    下面详细的讲解一下Flutter如何创建一个插件,并且教大家上传到Pub库,让大家也体验一把自己的库被别人使用的自豪感。

    AWeiLoveAndroid
  • PCL 1.7.2(vtk6.2) + VS2013 + win10_x64 配置及常见问题

    1.1 点击path添加(4个bin路径即包含dll,exe文件夹): (win10直接一行一行添加,win7;隔开)

    点云PCL博主
  • Python开源项目介绍:网站日志分析工具

    日志分析在web系统中故障排查、性能分析方面有着非常重要的作用。该工具的侧重点不是通常的PV,UV等展示,而是在指定时间段内提供细粒度(最小分钟级别,即一分钟内...

    Python中文社区
  • Python开源项目介绍:网站日志分析工具

    日志分析在web系统中故障排查、性能分析方面有着非常重要的作用。该工具的侧重点不是通常的PV,UV等展示,而是在指定时间段内提供细粒度(最小分钟级别,即一分钟内...

    小小科
  • 1078 字符串压缩与解压 (20 分)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

    韩旭051

扫码关注云+社区

领取腾讯云代金券