Java单元测试Powermockito和Mockito使用总结

目录
  • 依赖引入
  • PowerMockito的使用
    • 使用mockito来mock实例
    • mock对Redis的静态调用
    • mock单例类
    • mock私有方法
    • PowerMock跳过方法执行
  • 总结
  • 参考文档

最近公司在推进Java应用的单元测试,要求将单元测试的覆盖率提高到50%以上,保证上线代码充分自测。公司单元测试框架选用了Junit 4.12,Mock框架选用了Mockito和PowerMock,同时选用JaCoCo来做覆盖率检测,下面详细介绍一下我在使用这几个框架的一些经验。

依赖引入

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>2.8.9</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>1.7.4</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito2</artifactId>
    <version>1.7.4</version>
    <scope>test</scope>
</dependency>

PowerMockito的使用

Mockito、EasyMock、JMock等比较流行Mock框架有个共同的缺点,都不能mock静态、final、私有方法等,而PowerMock可以完美解决以上框架的不足,接下来让我们看看无所不能的PowerMock是如何解决上述问题,我们先从一个实例来看,下面的代码是需要单测的类,被测类中既有对Spring Bean的调用,同时又包含对Redis的静态读取,另外还有getInstance()单例类的使用,现在,我们一一来mock上述的外部类。

被单测类(主要测试queryStudentScoreByKeyword方法)

@Component
public class StudentService {

    @Resource
    private StudentDao studentDao;

    public List<StudentBo> queryStudentScoreByKeyword(String name) {
        System.out.println("invoke StudentService.queryStudentScoreByKeyword ...");

        List<StudentBo> cacheList = RedisUtils.getArray(name, StudentBo.class);
        if (CollectionUtils.isNotEmpty(cacheList)) {
            return cacheList;
        }
        String keyword = processKeyword(name);
        List<Student> students = studentDao.queryStudentByKeyWord(keyword);
        List<Integer> ids = students.stream().map(Student::getId).collect(Collectors.toList());
        List<Person> personList = SchoolManageProxy.getInstance().queryPerson(ids);

        List<StudentBo> studentBos = CommonUtils.toBo(personList, students);
        // 高亮结果
        highlightResult(studentBos, name);
        // 缓存到Redis
        RedisUtils.setArray(name, studentBos, 10 * 60);
        return studentBos;
    }

    private String processKeyword(String name) {
        System.out.println("invoke StudentService.processKeyword ...");
        String newName = name;
        // do somethings
        return newName;
    }

    private void highlightResult(List<StudentBo> result, String name) {
        System.out.println("invoke StudentService.highlightResult ...");
        // do keyword highlight
    }

}

单测类

@RunWith(PowerMockRunner.class)
@PrepareForTest({SchoolManageProxy.class, RedisUtils.class, StudentService.class})
// @PowerMockIgnore({"javax.management.*", "javax.net.ssl.*"})
@SuppressStaticInitializationFor({"cn.ganzhiqiang.ares.unittest.SchoolManageProxy"})
public class StudentServiceTest {

    @Mock
    private StudentDao mockStudentDao;

    @InjectMocks
    private StudentService studentServiceUnderTest;

    @Before
    public void setUp() {
        initMocks(this);
    }

    @Test
    public void testQueryStudentScoreByKeyword() throws Exception {
        studentServiceUnderTest = PowerMockito.spy(studentServiceUnderTest);
        PowerMockito.mockStatic(RedisUtils.class);
        PowerMockito.mockStatic(SchoolManageProxy.class);

        // mock单例调用
        SchoolManageProxy mockSchoolManageProxy = PowerMockito.mock(SchoolManageProxy.class);
        PowerMockito.when(SchoolManageProxy.getInstance()).thenReturn(mockSchoolManageProxy);
        when(mockSchoolManageProxy.queryPerson(anyList())).thenReturn(Collections.emptyList());

        // mock掉对Redis的静态调用
        PowerMockito.when(RedisUtils.getArray(eq("tom"), eq(StudentBo.class))).thenReturn(Collections.emptyList());
        // 显示的mock掉静态的void的方法(可以不mock)
        PowerMockito.doNothing().when(RedisUtils.class, "setArray", anyString(), anyList(), anyInt());

        // mock私有方法processKeyword
        PowerMockito.doReturn("tom").when(studentServiceUnderTest, "processKeyword", anyString());

        // 跳过私有方法highlightResult的执行
        PowerMockito.suppress(PowerMockito.method(StudentService.class, "highlightResult"));

        // 使用Mockito来mock服务的调用
        when(mockStudentDao.queryStudentByKeyWord(anyString())).thenReturn(Collections.emptyList());

        // Run the test
        final List<StudentBo> result = studentServiceUnderTest.queryStudentScoreByKeyword("tom");

    }
}

使用mockito来mock实例

首选我们先用Mockito来mock对Spring Bean的调用,Mockito.mock可以mock一个实例,我们这里选用@Mock注解,效果是一样的。

// 使用Mockito来mock服务的调用
when(mockStudentDao.queryStudentByKeyWord(anyString())).thenReturn(Collections.emptyList());

mock对Redis的静态调用

接下来我们使用PowerMock来mock对静态方法的调用,注意需要将RedisUtils类,加入@PrepareForTest注解中,我们既mock了getArray方法,也mock了setArray方法,其实setArray不需要mock这里显式的mock了

PowerMockito.mockStatic(RedisUtils.class);
// mock掉对Redis的静态调用
PowerMockito.when(RedisUtils.getArray(eq("tom"), eq(StudentBo.class))).thenReturn(Collections.emptyList());
// 显式的mock掉静态的void的方法(可以不mock)
PowerMockito.doNothing().when(RedisUtils.class, "setArray", anyString(), anyList(), anyInt());

mock单例类

mock单例类相对来说复杂一点,逻辑上先用Powermock mock出单例类,然后再给单例类的getInstance方法打桩,返回之前mock,再对mock类实际调用的方法打桩即可,代码如下

PowerMockito.mockStatic(SchoolManageProxy.class);
// Powermock mock出单例类
SchoolManageProxy mockSchoolManageProxy = PowerMockito.mock(SchoolManageProxy.class);
// 给单例类的getInstance方法打桩

PowerMockito.when(SchoolManageProxy.getInstance()).thenReturn(mockSchoolManageProxy);
// 对mock类queryPerson的方法打桩
when(mockSchoolManageProxy.queryPerson(anyList())).thenReturn(Collections.emptyList());

mock私有方法

可以看到queryStudentScoreByKeyword方法调用了该类的私有方法processKeyword,如果该方法耗时过长,使用powermock也可以mock该私有方法,需要注意的studentServiceUnderTest需要用spy()来mock

// mock 实例
// spy的标准是:如果不打桩,默认执行真实的方法,如果打桩则返回桩实现。
studentServiceUnderTest = PowerMockito.spy(studentServiceUnderTest);
// mock私有方法processKeyword
// doReturn(...) when(...)不做真实调用,但是when(...) thenReturn(...)还是会真实调用原方法,只是返回了指定的结果
PowerMockito.doReturn("tom").when(studentServiceUnderTest, "processKeyword", anyString());

PowerMock跳过方法执行

使用PowerMock也可以跳过私有方法的执行

// 跳过私有方法highlightResult的执行
PowerMockito.suppress(PowerMockito.method(StudentService.class, "highlightResult"));

总结

笔者之前写代码很少会写单测,自从公司强制要求提高单测覆盖率之后,虽然开发效率变慢了,但是确实引起我对单测的重视,进而研究了一下PowerMockito、Mokcito等Mock框架。Powermock之所以无所不能,是因为它使用了自定义的加载器和字节码操作技术,与此同时,它还十分简单易用,确实是个很优秀的框架。

Demo地址:https://github.com/LJWLgl/mock-data

参考文档

PowerMock
powermockito单元测试之深入实践
浅谈测试之PowerMock
无所不能的PowerMock,mock私有方法,静态方法,测试私有方法,final类
Mock和Spy的区别

到此这篇关于Java单元测试Powermockito和Mockito使用总结的文章就介绍到这了,更多相关Java Powermockito和Mockito 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解Java单元测试之JUnit篇

    单元测试是编写测试代码,应该准确.快速地保证程序基本模块的正确性. JUnit是Java单元测试框架,已经在Eclipse中默认安装. JUnit4 JUnit4通过注解的方式来识别测试方法.目前支持的主要注解有: @BeforeClass 全局只会执行一次,而且是第一个运行 @Before 在测试方法运行之前运行 @Test 测试方法 @After 在测试方法运行之后允许 @AfterClass 全局只会执行一次,而且是最后一个运行 @Ignore 忽略此方法 下面基于Eclipse介绍JUn

  • Java单元测试Mockito的使用详解

    Mockito简介 调用mock对象的方法时,不会执行真实的方法,而是返回类型的默认值,如object返回null, int返回0等,否则通过指定when(方法).thenReturn(value)来指定方法的返回值.同时mock对象可以进行跟踪,使用verify方法看是否已经被调用过.而spy对象,默认会执行真实方法,返回值可以通过when.thenReturn进行覆盖.可见mock只要避开了执行一些方法,直接返回指定的值,方便做其他测试. Service测试用例 需要的依赖 <depende

  • java编程之单元测试(Junit)实例分析(附实例源码)

    本文实例讲述了java编程之单元测试.分享给大家供大家参考,具体如下: 完整实例代码代码点击此处本站下载. 在有些时候,我们需要对我们自己编写的代码进行单元测试(好处是,减少后期维护的精力和费用),这是一些最基本的模块测试.当然,在进行单元测试的同时也必然得清楚我们测试的代码的内部逻辑实现,这样在测试的时候才能清楚地将我们希望代码逻辑实现得到的结果和测试实际得到的结果进行验证对比. 废话少说,上代码: 首先创建一个java工程,在工程中创建一个被单元测试的Student数据类,如下: packa

  • 解决java junit单元测试@Test报错的问题

    在我们在myeclips里使用junit测试工具时有时会遇到错误,这是什么原因呢? 导致问题的原因通常有下面几个: (1)没有导入jar包 (2)导入jar包版本太低 (3)注意@Test要写在方法上面 如果不是几种问题,那便试试下面的解决方案: 1.在项目上点击右键,出现下图内容,选择properties 2.出现如下对话框,点击java build path,再选择add Library 3.之后如下图操作 4.选择junit4,点击finish,配置完毕. 以上这篇解决java junit

  • java单元测试JUnit框架原理与用法实例教程

    本文实例讲述了java单元测试JUnit框架原理与用法.分享给大家供大家参考,具体如下: 1 简介 JUnit是一个Java语言的单元测试框架,它由 Kent Beck 和 Erich Gamma 建立,逐渐成为 xUnit 家族中最为成功的一个. JUnit有它自己的JUnit扩展生态圈,多数Java的开发环境都已经集成了JUnit作为单元测试的工具.在这里,一个单元可以是一个方法.类.包或者子系统.因此,单元测试是指对代码中的最小可测试单元进行检查和验证,以便确保它们正常工作.例如,我们可以

  • 详解Java单元测试Junit框架实例

    问题: 1.目前测试存在的问题 2.Junit注意的细节 3.Junit使用规范 4.断言 5.案例 junit(单元测试框架) 1.目前存在的问题 1.目前的测试方法如果需要测试,都需要在main方法上调用 2.目前的结果都需要我们人工比对 2.Junit 注意的细节 1.如果使用junit测试一个方法的时候,在junit窗口上显示绿色那么表示测试正确,如果显示了红色,则代表该方法测试出现了异常不通过 2.如果点击方法名.类名.包名.工程名运行junit分别测试的是对于的方法,类.包中的所有类

  • 深入学习Java单元测试(Junit+Mock+代码覆盖率)

    前言 单元测试是编写测试代码,用来检测特定的.明确的.细颗粒的功能.单元测试并不一定保证程序功能是正确的,更不保证整体业务是准备的. 单元测试不仅仅用来保证当前代码的正确性,更重要的是用来保证代码修复.改进或重构之后的正确性. 一般来说,单元测试任务包括 1.接口功能测试:用来保证接口功能的正确性. 2.局部数据结构测试(不常用):用来保证接口中的数据结构是正确的 1.比如变量有无初始值 2.变量是否溢出 3.边界条件测试 1.变量没有赋值(即为NULL) 2.变量是数值(或字符) 1.主要边界

  • Java Junit单元测试实例详解

    测试分类 黑盒测试: 不需要写代码, 给输入值, 看程序是否能够输出期望的值 白盒测试: 需要写代码, 关注程序具体的执行流程 Junit 是白盒测试的一种 1.定义一个测试类 (测试用例) 包名: xxx.xxx.test 测试类名: 被测试的类型 + Test 2.定义测试方法: 可以单独运行 方法名: test + 测试的方法名 返回值: void 参数列表: 空参 3.给方法加 @Test 注解 (需要导入 Junit 环境依赖) 4.判定结果: 红色: 失败, 绿色: 成功 一般使用断

  • JAVA中单元测试的常用方式(小结)

    什么是单元测试 单元测试(英语:Unit Testing)又称为模块测试, 是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作.程序单元是应用的最小可测试部件.在过程化编程中,一个单元就是单个程序.函数.过程等:对于面向对象编程,最小单元就是方法,包括基类(超类).抽象类.或者派生类(子类)中的方法. 通常来说,程序员每修改一次程序就会进行最少一次单元测试,在编写程序的过程中前后很可能要进行多次单元测试,以证实程序达到软件规格书要求的工作目标,没有程序错误:虽然单元测试不是什么必须的

  • Java单元测试Powermockito和Mockito使用总结

    目录 依赖引入 PowerMockito的使用 使用mockito来mock实例 mock对Redis的静态调用 mock单例类 mock私有方法 PowerMock跳过方法执行 总结 参考文档 最近公司在推进Java应用的单元测试,要求将单元测试的覆盖率提高到50%以上,保证上线代码充分自测.公司单元测试框架选用了Junit 4.12,Mock框架选用了Mockito和PowerMock,同时选用JaCoCo来做覆盖率检测,下面详细介绍一下我在使用这几个框架的一些经验. 依赖引入 <depen

  • 快速上手Java单元测试框架JUnit5

    为什么学JUnit5 Java技术栈的单元测试框架有两个:JUnit和TestNG,有种说法是TestNG比JUnit更强大,学TestNG就够了,但是当我打开GitHub看到star的时候,犹豫了: JUnit TestNG 相差了足足有3K之多.带着这个困惑,我在网上查阅了一番资料,原来JUnit5相较于JUnit4有了重大升级,已经包含了TestNG的所有功能.为了坚定我学JUnit的想法,我咨询了身边做Java开发的朋友,开发写UT都是用的JUnit.这两个理由足以让我开始对JUnit5

  • java 单元测试 对h2数据库数据清理方式

    目录 java 单元测试 对h2数据库数据清理 前因 junit单元测试使用H2内存数据库 首先导入H2内存数据库 其次使用H2数据源模拟Oracle 下面来写个Junit4的单元测试类例子 java 单元测试 对h2数据库数据清理 前因 写测试框架的时候使用的精简测试框架不需要启动整个springboot,并不支持@Transactional测试后回滚h2数据库,而是在基础测试类里声明cleandb函数供使用,这就需要适配任意表的数据清除,不过更推荐不清理,以方法名为id使数据不重复即可 tr

  • 如何使用JaCoCo分析java单元测试覆盖率

    前言 随着敏捷开发的流行,编写单元测试已经成为业界共识.但如何来衡量单元测试的质量呢?有些管理者片面追求单元测试的数量,导致底下的开发人员投机取巧,编写出大量的重复测试,数量上去了,质量却依然原地踏步.相比单纯追求单元测试的数量,分析单元测试的代码覆盖率是一种更为可行的方式. JaCoCo(Java Code Coverage)就是一种分析单元测试覆盖率的工具,使用它运行单元测试后,可以给出代码中哪些部分被单元测试测到,哪些部分没有没测到,并且给出整个项目的单元测试覆盖情况百分比,看上去一目了然

  • Spring Boot单元测试中使用mockito框架mock掉整个RedisTemplate的示例

    概述 当我们使用单元测试来验证应用程序代码时,如果代码中需要访问Redis,那么为了保证单元测试不依赖Redis,需要将整个Redis mock掉.在Spring Boot中结合mockito很容易做到这一点,如下代码: import org.mockito.Mockito; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration

  • Java测试框架Mockito的简明教程

    什么是 Mock 测试 Mock 测试就是在测试过程中,对于某些不容易构造(如 HttpServletRequest 必须在Servlet 容器中才能构造出来)或者不容易获取比较复杂的对象(如 JDBC 中的ResultSet 对象),用一个虚拟的对象(Mock 对象)来创建以便测试的测试方法. Mock 最大的功能是帮你把单元测试的耦合分解开,如果你的代码对另一个类或者接口有依赖,它能够帮你模拟这些依赖,并帮你验证所调用的依赖的行为. 比如一段代码有这样的依赖: 当我们需要测试A类的时候,如果

随机推荐