有了它(powermock)再也不担心单元测试不达标了
为什么要写单元测试
- 优点:单元测试可以减少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)再也不担心单元测试不达标了的更多相关文章
- 保姆级神器 Maven,再也不用担心项目构建搞崩了
今天来给大家介绍一款项目构建神器--Maven,不仅能帮我们自动化构建,还能够抽象构建过程,提供构建任务实现:它跨平台,对外提供了一致的操作接口,这一切足以使它成为优秀的.流行的构建工具,从此以后,再 ...
- 妈妈再也不用担心别人问我是否真正用过redis了
1. Memcache与Redis的区别 1.1. 存储方式不同 1.2. 数据支持类型 1.3. 使用底层模型不同 2. Redis支持的数据类型 3. Redis的回收策略 4. Redis小命令 ...
- 锋利的js之妈妈再也不用担心我找错钱了
用js实现收银功能. <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <hea ...
- 【阿里云产品公测】离线归档OAS,再也不用担心备份空间了
[阿里云产品公测]离线归档OAS,再也不用担心备份空间了 作者:阿里云用户莫须有3i 1 起步 1.1 初识OAS 啥是OAS,请看官方说明: 引用: 开放归档服务(Open Archive Se ...
- 有了 tldr,妈妈再也不用担心我记不住命令了
引言 有一次我在培训时说「程序员要善于使用 Terminal 以提高开发效率」,一位程序员反驳道:「这是 21 世纪,我们为什么要用落后的命令行,而不是先进的 GUI?」 是的,在一些人眼里,这个黑黑 ...
- 妈妈再也不用担心我使用git了
妈妈再也不用担心我使用git了 Dec 29, 2014 git git由于其灵活,速度快,离线工作等特点而倍受青睐,下面一步步来总结下git的基本命令和常用操作. 安装msysgit 下载地址:ms ...
- 利用CH341A编程器刷新BIOS,恢复BIOS,妈妈再也不用担心BIOS刷坏了
前几天,修电脑主析就捣鼓刷BIOS,结果刷完黑屏开不了机,立刻意识到完了,BIOS刷错了.就从网上查资料,各种方法试了个遍,什么用处都没有.终于功夫不负有心人,找到了编码器,知道了怎么用.下面看看具体 ...
- python爬虫07 | 有了 BeautifulSoup ,妈妈再也不用担心我的正则表达式了
我们上次做了 你的第一个爬虫,爬取当当网 Top 500 本五星好评书籍 有些朋友觉得 利用正则表达式去提取信息 太特么麻烦了 有没有什么别的方式 更方便过滤我们想要的内容啊 emmmm 你还别说 还 ...
- zzulioj--1841--so easy!麻麻再也不用担心我的数学了!(数学水题)
1841: so easy!麻麻再也不用担心我的数学了! Time Limit: 1 Sec Memory Limit: 128 MB Submit: 27 Solved: 15 SubmitSt ...
随机推荐
- mininet + opendaylight环境配置
环境配置 ubuntu18.04 镜像 mininet2.2.2 apt-get install mininet 但这种安装只是TLS版本的mininet,与最新版本在功能上有所差距. 控制器(ope ...
- form 表单上传文件及传输数据的编码格式
form中的 action 控制请求往什么地方提交 method 请求方式 如果不写默认是get 请求 如果想传文件 必须要把默认的urlencoded的改成enctype="multi ...
- vue----(组件)子组件和父组件
1.组件的定义 1.定义组件并引用 2.父组件向子组件传值 3.子组件向父组件传值 什么是组件 1.Html中有组件,是一段可以被复用的结构代码 2.Css中有组件,是一段可以被复用的样式 3.Js中 ...
- 为什么 redo log 具有 crash-safe 的能力,是 binlog 无法替代的?
昨天在复习 MySQL 日志相关的知识,学的东西过一段时间后就会遗忘,遗忘后再重新思考,往往会有新的收获.想到几个问题,把它记录下来. 为什么 redo log 具有 crash-safe 的能力,而 ...
- Linux之【安装系统后的调优和安全设置】
关闭SElinux功能 •修改配置文件使其永远生效 第一种修改方法vi vi /etc/sysconfig/selinuc 或者 vi /etc/selinux/config修改: SELINUX=d ...
- R语言无网络安装R包,彻底解决依赖问题!
R version: 3.5.3, 3.6.3 更新日期: 2020-9-10 大家测试后多提建议哈, 有问题我会持续更新的 在工作中,我们使用的服务器通常是不能联外网的,这在安装R包的时候产生了巨大 ...
- java的jdk8新特性optional怎么样使用
从 Java 8 引入的一个很有趣的特性是 Optional 类.Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException) -- 每个 Java 程序员都 ...
- Mac开发必设置的Finder设置项,你设置了吗?
1.显示标签页.显示路径栏.显示状态栏的设置位置,在访达->显示-> 显示状态栏 个人三个都设置了,但是觉得显示状态栏用的并不多,反而多一行,下面是显示状态栏的效果,主要可以一眼看出有多少 ...
- C++详解(8-9)
八. C++函数的高级特性 对比于C语言的函数,C++增加了重载(overloaded).内联(inline).const和virtual四种新机制.其中重载和内联机制既可用于全局函数也可用于类的成员 ...
- mysql聚簇索引和非聚簇索引
聚簇索引 InnoDB使用的是聚簇索引 将数据与主键索引放在了一起,索引的叶子节点保存了行数据,找到了主键索引,即找到了行数据. 辅助索引记录了主键的位置,所以查询where name= xxx 时, ...