为什么要写单元测试

  • 优点:单元测试可以减少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. Window .NetCore Nginx

    1.首先去官网下载Nginx 官网地址:http://nginx.org/en/download.html 2.下载好之后,我的文件是放在D:\InstallFiles\Nginx\nginx-1.1 ...

  2. 转载的一篇文章eclipse添加插件

    eclipse没有(添加)"Dynamic Web Project"选项的方法 转载海边的第八只螃蟹 最后发布于2015-11-24 21:24:15 阅读数 40814  收藏 ...

  3. 从docker介绍及其简介

    一.引言 1.我本地代码运行没问题啊,但是别人机器运行不了,从而导致环境不一致的问题 2.那个兄弟又写死循环了,怎么这么卡,在多用户的操作系统下,会相互影响. 天猫双十一的情况下,用户量暴涨,从而导致 ...

  4. MySQL ERROR 1040: Too many connections

    如题,本章主要讲下当服务器出现 ERROR 1040: Too many connections错误时的一些处理心得. max_connections查看 ## 查看最大连接数 SHOW VARIAB ...

  5. NFS服务配置总结

    目录 介绍 原理 适用场景 安装 NFS服务端 NFS客户端 配置 服务端配置 客户端配置 介绍 NFS就是Network File System的缩写,它最大的功能就是可以通过网络,让不同的机器.不 ...

  6. PyQt(Python+Qt)学习随笔:Qt Designer中toolBar的movable属性

    1.概述 movable属性用来确认toolBar是否可以移动,如果设置为可移动,则toolBar可以在主窗口范围内拖拽移动. 2.访问方法 通过isMovable().setMovable(bool ...

  7. [BJDCTF 2nd]假猪套天下第一 && [BJDCTF2020]Easy MD5

    [BJDCTF 2nd]假猪套天下第一 假猪套是一个梗吗? 进入题目,是一个登录界面,输入admin的话会返回错误,登录不成功,其余用户可以正常登陆 以为是注入,简单测试了一下没有什么效果 抓包查看信 ...

  8. SASRec 实践

    SASRec--2018,ICDM,论文<Self-Attentive Sequential Recommendation> 源代码链接:https://github.com/kang20 ...

  9. C#声明一个100大小的数组 随机生成1-100之间不重复的数

    面试题:C#声明一个100大小的数组 随机生成1-100之间不重复的数下面是C#的实现方式,编译测试通过 public static void InsertRandomArray() { int[] ...

  10. Symbol类型是不可枚举的

    const info = { [Symbol('a')]: 'b' } console.log(info)//{Symbol('a'): 'b'} console.log(Object.keys(in ...