为什么要写单元测试

  • 优点:单元测试可以减少bug率,提升代码的质量。还可以通过单元测试来熟悉业务。
  • 公司硬性要求:有些公司可能还会强制要求,每次新增代码、或者变更代码单测覆盖率要达到多少比例才能申请代码合并请求。

选择哪个单元测试框架

目前应用比较普遍的java单元测试工具 junit4+Mock(Mockito、jmock、EasyMock、powermock)。为什么会选择powermock?

在做单元测试的时候,我们会发现我们要测试的方法会有很多外部依赖的对象或者一些其他服务的调用比如说(发送邮件,网络通讯,soa调用)。 而我们没法控制这些外部依赖的对象。 为了解决这个问题,我们需要用到Mock来模拟这些外部依赖的对象,从而控制它们。只关心我们自己的业务逻辑是否正确。而这时powermock就起作用了,它不仅可以mock外部的依赖,还可以mock私有方法、final方法,总之它的功能很强大。

什么是powerMocker

PowerMock是一个框架,它以更强大的功能扩展了其他模拟库,例如EasyMock。 PowerMock使用自定义的类加载器和字节码操作来模拟静态方法,构造函数, 最终类和方法,私有方法,删除静态初始化程序等。通过使用自定义类加载器,无需对IDE或持续集成服务器进行任何更改,从而简化了采用过程。熟悉受支持的模拟框架的开发人员会发现PowerMock易于使用,因为整个期望API都是相同的,

无论是静态方法还是构造函数。PowerMock 旨在通过少量方法和注释扩展现有的API,以启用额外的功能。

常用注解

  • @RunWith(PowerMockRunner.class)

    告诉JUnit使用PowerMockRunner进行测试
  • @PrepareForTest({DemoDao.class})

    所有需要测试的类列在此处,适用于模拟final类或有final, private, static, native方法的类
  • @PowerMockIgnore({“javax.management.", "javax.net.ssl.”})

    为了解决使用powermock后,提示classloader错误
  • @SuppressStaticInitializationFor

    不让静态代码加载

    其他更多注解可以参考:https://github.com/powermock/powermock/wiki/Suppress-Unwanted-Behavior

如何开始

JUnit 4.4及以上
<properties>
<powermock.version>2.0.2</powermock.version>
</properties>
<dependencies>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

powerMock样例

这是一个需要被mock的类里面有私有方法、静态方法、等等下面一一来演示各个方法的mock功能。

/**
*
* @Date: 2020/3/31
* @Description:
*/
@Repository
public class DemoDao {
public String mockPublicMethod(String type) throws Throwable {
throw new Throwable();
}
public final String mockFinalMethod(String type) throws Throwable {
throw new Throwable();
}
public static String mockStaticMethod(String type) throws Throwable {
throw new Throwable();
}
}
/**
* @Date: 2020/3/31 11:34
* @Description:
*/
@Component
public class DemoService extends AbstractDemo{ @Autowired
private DemoDao demoDao; public String mockPublicMethod() throws Throwable {
return demoDao.mockPublicMethod("demo");
} public String mockFinalMethod() throws Throwable {
return demoDao.mockFinalMethod("demo");
} public String mockStaticMethod() throws Throwable {
return DemoDao.mockStaticMethod("demo");
} private String callPrivateMethod(String type) {
return type;
} public String mockPublicMethodCallPrivateMethod(String type) throws Throwable {
return callPrivateMethodThrowable(type);
} private String callPrivateMethodThrowable(String type) throws Throwable {
throw new Throwable();
} public String mockExtendMethod(String type) throws Throwable {
return getExtendMethod();
} public static String UUID = "uuid";
}
mock普通公共方法
   /**
* @Date: 2020/4/24
* @Description:
*/
@RunWith(PowerMockRunner.class)
public class DemoServiceTest { @InjectMocks
private DemoService demoService;
@Mock
private DemoDao demoDao; /**
* mock 普通方法
* @throws Throwable
*/
@Test
public void mockPublicMethod() throws Throwable {
String type = UUID.randomUUID().toString();
PowerMockito.when(demoDao.mockPublicMethod(any())).thenReturn(type);
String result = demoService.mockPublicMethod();
Assert.assertEquals(type, result);
}
mock Final方法

跟普通方法是一样的,唯一的区别是需要在类上加入PrepareForTest注解

    @RunWith(PowerMockRunner.class)
@PrepareForTest(DemoDao.class)
public class DemoServiceTest { @InjectMocks
private DemoService demoService;
@Mock
private DemoDao demoDao; /**
* mock final方法
* @throws Throwable
*/
@Test
public void mockFinalMethod() throws Throwable {
String type = UUID.randomUUID().toString();
PowerMockito.when(demoDao.mockFinalMethod(any())).thenReturn(type);
String result = demoService.mockFinalMethod();
Assert.assertEquals(type, result);
}
mock静态方法(使用 PowerMockito.mockStatic)被mock的类也要用PrepareForTest注解修饰。
  @RunWith(PowerMockRunner.class)
@PrepareForTest(DemoDao.class)
public class DemoServiceTest { @InjectMocks
private DemoService demoService;
@Mock
private DemoDao demoDao;
/**
* mock 静态方法
* @throws Throwable
*/
@Test
public void mockStaticMethod() throws Throwable {
String type = UUID.randomUUID().toString();
PowerMockito.mockStatic(DemoDao.class);
PowerMockito.when(DemoDao.mockStaticMethod(any())).thenReturn(type);
String result = demoService.mockStaticMethod();
Assert.assertEquals(type, result);
}
调用 private方法
    /**
* 调用私有方法
*
* @throws Throwable
*/
/**
* 调用私有方法
*
* @throws Throwable
*/
@Test
public void callPrivateMethod() throws Throwable {
// 第一种方式
String type = UUID.randomUUID().toString();
Method method = PowerMockito.method(DemoService.class, "callPrivateMethod", String.class);
String result = (String) method.invoke(demoService, type);
Assert.assertEquals(type, result); //第二种方式
String result1 = Whitebox.invokeMethod(demoService, "callPrivateMethod", type);
Assert.assertEquals(type, result1);
}
mock 私有方法(被mock的类也要用PrepareForTest注解修饰。)
    /**
* mock私有方法
*
* @throws Throwable
*/
@Test
public void mockPrivateMethod() throws Throwable {
String type = UUID.randomUUID().toString();
// 重点这一句
demoService = PowerMockito.spy(demoService);
PowerMockito.doReturn(type).when(demoService,"callPrivateMethodThrowable",type);
String result = demoService.mockPublicMethodCallPrivateMethod(type);
Assert.assertEquals(type, result);
}
mock父类方法
     /**
* mock父类方法
*
* @throws Throwable
*/
@Test
public void mockExtendMethod() throws Throwable {
String type = UUID.randomUUID().toString();
// 需要mock的父类的方法
Method method = PowerMockito.method(AbstractDemo.class, "getExtendMethod");
// InvocationHandler
PowerMockito.replace(method).with((proxy, method1, args) -> type);
String result = demoService.mockExtendMethod(type);
Assert.assertEquals(type, result);
}
mock构造方法
     public DemoService() {
throw new NullPointerException();
}
@Test
public void mockConstructorMethod() throws Throwable {
PowerMockito.whenNew(DemoService.class).withNoArguments().thenReturn(demoService);
}
mock字段
        /**
* mock 字段
*/
@Test
public void mockFiled(){
String uuid = UUID.randomUUID().toString();
Whitebox.setInternalState(DemoService.class, "UUID",uuid);
Assert.assertEquals(DemoService.UUID, uuid);
}
mock new对象调用的方法
		class DemoDao {
public void newMethod(){
DemoDao demoDao = new DemoDao();
demodao.init();
}
public void init(){
throw new NullPointerException();
}
}
DemoDao demoDao= PowerMockito.mock(DemoDao .class);
PowerMockito.whenNew(DemoDao .class).withNoArguments().thenReturn(demoDao);
PowerMockito.when(demoDao.newMethod()).thenReturn(null);

结束

  • 由于自己才疏学浅,难免会有纰漏,假如你发现了错误的地方,还望留言给我指出来,我会对其加以修正。
  • 如果你觉得文章还不错,你的转发、分享、赞赏、点赞、留言就是对我最大的鼓励。
  • 感谢您的阅读,十分欢迎并感谢您的关注。

有了它(powermock)再也不担心单元测试不达标了的更多相关文章

  1. 保姆级神器 Maven,再也不用担心项目构建搞崩了

    今天来给大家介绍一款项目构建神器--Maven,不仅能帮我们自动化构建,还能够抽象构建过程,提供构建任务实现:它跨平台,对外提供了一致的操作接口,这一切足以使它成为优秀的.流行的构建工具,从此以后,再 ...

  2. 妈妈再也不用担心别人问我是否真正用过redis了

    1. Memcache与Redis的区别 1.1. 存储方式不同 1.2. 数据支持类型 1.3. 使用底层模型不同 2. Redis支持的数据类型 3. Redis的回收策略 4. Redis小命令 ...

  3. 锋利的js之妈妈再也不用担心我找错钱了

    用js实现收银功能. <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <hea ...

  4. 【阿里云产品公测】离线归档OAS,再也不用担心备份空间了

    [阿里云产品公测]离线归档OAS,再也不用担心备份空间了 作者:阿里云用户莫须有3i 1 起步  1.1 初识OAS  啥是OAS,请看官方说明: 引用: 开放归档服务(Open Archive Se ...

  5. 有了 tldr,妈妈再也不用担心我记不住命令了

    引言 有一次我在培训时说「程序员要善于使用 Terminal 以提高开发效率」,一位程序员反驳道:「这是 21 世纪,我们为什么要用落后的命令行,而不是先进的 GUI?」 是的,在一些人眼里,这个黑黑 ...

  6. 妈妈再也不用担心我使用git了

    妈妈再也不用担心我使用git了 Dec 29, 2014 git git由于其灵活,速度快,离线工作等特点而倍受青睐,下面一步步来总结下git的基本命令和常用操作. 安装msysgit 下载地址:ms ...

  7. 利用CH341A编程器刷新BIOS,恢复BIOS,妈妈再也不用担心BIOS刷坏了

    前几天,修电脑主析就捣鼓刷BIOS,结果刷完黑屏开不了机,立刻意识到完了,BIOS刷错了.就从网上查资料,各种方法试了个遍,什么用处都没有.终于功夫不负有心人,找到了编码器,知道了怎么用.下面看看具体 ...

  8. python爬虫07 | 有了 BeautifulSoup ,妈妈再也不用担心我的正则表达式了

    我们上次做了 你的第一个爬虫,爬取当当网 Top 500 本五星好评书籍 有些朋友觉得 利用正则表达式去提取信息 太特么麻烦了 有没有什么别的方式 更方便过滤我们想要的内容啊 emmmm 你还别说 还 ...

  9. zzulioj--1841--so easy!麻麻再也不用担心我的数学了!(数学水题)

    1841: so easy!麻麻再也不用担心我的数学了! Time Limit: 1 Sec  Memory Limit: 128 MB Submit: 27  Solved: 15 SubmitSt ...

随机推荐

  1. python应用(6):函数

    在流程很简单的时候,或者流程不简单但我们不需要考虑开发维护成本的时候,平面地组织你的代码就够了,不用费脑子,不需要考虑层次或重用的东西.当事情变得越来越复杂时,当同行对代码质量要求越来越高时,有一些重 ...

  2. Spring Boot + Redis 初体验

    本文测试环境: Spring Boot 2.1.4.RELEASE + Redis 5.0.4 + CentOS 7 让程序先 run 起来 安装及配置 Redis 参考: How To Instal ...

  3. Docker学习—Stack

    前言: 前一篇了解Docker使用Swarm集群部署方式,并创建服务到Swarm集群中:如果在集群部署过程中存在大量服务部署.编排那么该如何处理呢? 那么就需要了解Docker Stack了. 1.D ...

  4. 虚拟机(centos7)网络配置

    修改网络配置(ip等) VWware右上角,选择编辑---虚拟网络编辑器 可以看到子网ip是 192.168.1.0,那么我们分配ip地址就可以从此ip段开始. 再点击NAT设置 这里的网关我们稍后会 ...

  5. [SWPU2019]Web1 空格过滤用/**/ 注释过滤闭合单引号 imformation_schema.columns/tables过滤 用5.7新特性 或无名注入(此处database()不能用)

    0x00 知识点 二次注入流程分析 二次注入漏洞在CTF中常见于留言板和注册登录功能,简单来说可以分为两个步骤: 插入恶意数据(发布帖子,注册账号),用mysql_escape_string()函数对 ...

  6. python中的Restful

    哇,昨天组里进行总结的时候,小哥哥和小姐姐真是把我给秀到了,跟他们一比,我总结的太垃圾了,嘤嘤嘤.因为我平常不怎么总结,总结的话,有word还有纸质的,现在偏向于纸质,因为可以练练字.个人观点是,掌握 ...

  7. nodjs html 转 pdf

    var fs = require('fs');var pdf = require('html-pdf');//模块//读取html 文件 var html = fs.readFileSync('./s ...

  8. P4317 花神的数论题,关于luogu题解粉兔做法的理解

    link 题意 设 \(\text{sum}(i)\) 表示 \(i\) 的二进制表示中 \(1\) 的个数.给出一个正整数 \(N\) ,求 \(\prod_{i=1}^{N}\text{sum}( ...

  9. 题解-CF1437F Emotional Fishermen

    题面 CF1437F Emotional Fishermen 给 \(n\) 个数的序列 \(a_i\),求有多少种 \(n\) 个数的排列 \(p_i\),使得 \[\frac{a_{p_i}}{\ ...

  10. NOIP2017 D1T3 逛公园

    发现 \(K\) 很小,不妨设置一个 \(O(NK)\) 的 \(DP\). 发现可行的最短路必须满足是 \(d <= dis <= d + K\). 由逆向思维,则是从某点出发,可以消耗 ...