springboot集成mockito与powermock
mockito大家都比较熟悉了,存在或者不存在,都不要紧,mockito让你有一种只要一出手,就知道有没有的感觉。但是它也不是万能的,比如静态方法、私有方法,它就无能为力了。这是为什么呢?当然不是mockito的框架或现有技术解决不了,而是出于某些原因或立场,比如测试理念观点。甚至在mockito的FAQ中,作者明确了每一项未实现的功能不支持的原因,或者干脆说已经有别的工具实现了,需要的话,去用那个工具吧,我不愿意重复造轮子。
当然实现这些也并非轻而意举,比如如何mock final类,特别是jdk中的final类,比如String。但作为系统类,在任何时候都不应该可以被修改(即使是有办法修改,也不建议去修改,也没有必要修改,否则重新设计一门新语言即可),特别是对于java.lang包下的类,如基本的数据类型Integer、Long等。java agent可以修改由AppClassLoader加载的类,而endorsed技术也只允许覆盖在有限的限制列表中的类。而powermock采取的方案是,如果需要mock的是系统类的final方法和静态方法,PowerMock不会直接修改系统类的class文件,而是修改调用系统类的class文件,以满足mock需求。
mockito的实现原理是用asm给需要mock的对象生成对应的代理对象,然后使用mock出来的对象即可。而在spring框架中,SpyBean与MockBean的原理也是一样,只不过还需要多做一步,就是用mock后的对象替换容器中原有的对象。
尽管如此,但mockito仍然有一些限制,罗列及翻译如下:
Mockito 2.x specific limitations
Mockito 2.x限制
Requires Java 6+
需JDK 6以上版本
Cannot mock static methods
不支持mock静态方法
Cannot mock constructors
不支持mock构造函数
Cannot mock equals(), hashCode(). Firstly, you should not mock those methods. Secondly, Mockito defines and depends upon a specific implementation of these methods. Redefining them might break Mockito.
Mocking is only possible on VMs that are supported by Objenesis. Don't worry, most VMs should work just fine.
不支持mock equals与hashCode方法。mockito认为不应该mock这两个方法,因为mockito的实现方案依赖于这两个方法。重新定义这两个方法可以会导致mockito异常。同时mocking只能在被Objenesis支持的vm上运行,目前在大部分vm上都能运行得很好。(Objenesis是一个使用旁门左道创建类实例的库,除了调用类的构造函数)
Spying on real methods where real implementation references outer Class via OuterClass.this is impossible. Don't worry, this is extremely rare case.
不支持当内部类的某个方法实现中引用外部OuterClass.this的情形。但不需要担心,这样的例子真的很少见。
Can I mock static methods?
支持mock静态方法吗
No. Mockito prefers object orientation and dependency injection over static, procedural code that is hard to understand & change. If you deal with scary legacy code you can use JMockit or Powermock to mock static methods.
不支持,Mockito更倾向于在面向对象与依赖注入的层面上mock,而不是mock静态方法,静态方法这种面向过程的代码比较难理解与改变。如果你要处理这些恐怖的遗留代码,那么请使用JMockit或者Powermock来mock静态方法。
Can I mock private methods?
支持mock私有方法吗
No. From the standpoint of testing... private methods don't exist. More about private methods here.
不支持。从测试方法的观点来看,其实私有方法是不存在的(需要测试的都是公开的方法)。更多关于私有方法的观点讲参照这里(注:原文这里是个链接)。
Why Mockito doesn't mock private methods?
为什么Mockito不支持mock私有方法
Firstly, we are not dogmatic about mocking private methods. We just don't care about private methods because from the standpoint of testing, private methods don't exist. Here are a couple of reasons Mockito doesn't mock private methods:
首先,关于mock私有方法论断,不是我们自以为是。我们不关注私有方法是因为测试方法相关的观点,私有方法是不存在的。这里有一些关于Mockito不支持mock私有方法的原因:
1. It requires hacking of classloaders that is never bullet proof and it changes the API (you must use custom test runner, annotate the class, etc.).
mock私有方法需要侵入classloader,虽然classloader并非刀枪不入,但是这样会改变你使用Mockito API的方式(比如使用自定义的test runner,使用特殊的注解等)
翻译加注:因为私有方法不可见,无法使用原有的mockito语法实现mock,所以必然需要其他途径来达到目的,而powermock使用自定义的classloader与runner来实现。静态方法的mock也是同样的原因,因为没有对象可以被mock,只能通过classloader做文章,自定义的classloader想返回什么字节码就返回什么字节码。
2.It is very easy to work around - just change the visibility of method from private to package-protected (or protected).
其实mock私有方法可以很简易地解决,只需要改变方法的可见性,如将private改为default或protected。
3. It requires the team to spend time implementing & maintaining it. And it does not make sense given point (2) and a fact that it is already implemented in different tool (powermock).
它需要Mockito团队花时间去实现并且维护它,但是基于观点2这又讲不过去(即说不服自己),并且事实上已经有不同的工具(powermock)实现它了。
4. Finally... Mocking private methods is a hint that there is something wrong with Object Oriented understanding. In OO you want objects (or roles) to collaborate, not methods. Forget about pascal & procedural code. Think in objects.
最后,对私有方法进行mock意味着对面向对象编程的概念理解有偏差。在面向对象的理念中你需要的是对象(或角色)来协作,而不是方法。忘记pascal,忘记面向过程编程吧,以面向对象的方式来思考。
public class MyUtil {
public static String hello() {
return "hello";
}
}
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
private String getName2() {
return this.name;
}
public String getName() {
return getName2();
}
}
package com.kidshelloworld.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
package com.kidshelloworld.test;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.modules.junit4.PowerMockRunnerDelegate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
@RunWith(PowerMockRunner.class)
@PowerMockIgnore(value = { "javax.management.*", "javax.net.ssl.*", "javax.net.SocketFactory" })
@ActiveProfiles(value = { "testcase" })
@PrepareForTest(value = { MyUtil.class, Person.class })
@SpringBootTest(classes = TestApplication.class)
public class TestMyUtil {
private Person spyPerson;
@autowired
public void prepare() {
PowerMockito.mockStatic(MyUtil.class);
Mockito.when(MyUtil.hello()).thenReturn("hi");
spyPerson = PowerMockito.spy(new Person("xyz");
}
@Test
public void testHello() {
Assert.assertEquals("hi", MyUtil.hello());
}
@Test
public void testGetName() {
PowerMockito.when(spy, "getName2").thenReturn("abc");
String name = spy.getName();
Assert.assertEquals("abc", name);
}
}
powermock使用了自定义的classloader来解决mock静态方法与私有方法的问题,因此其会为加了PrepareForTest注解的类生成对应的classloader来加载用到的类,这样就可能会导致其与系统的classloader加载了相同的类,导致类型转换失败,PowerMockIgnore注解则是告诉powermock放弃加载指定的这些类。
同时powermock使用了自定义的PowerMockRunner,与spring集成时,可以代理至SpringJUnit4ClassRunner。
欢迎关注个人公众号

springboot集成mockito与powermock的更多相关文章
- 使用MRUnit,Mockito和PowerMock进行Hadoop MapReduce作业的单元测试
0.preliminary 环境搭建 Setup development environment Download the latest version of MRUnit jar from Apac ...
- 【springBoot】springBoot集成redis的key,value序列化的相关问题
使用的是maven工程 springBoot集成redis默认使用的是注解,在官方文档中只需要2步; 1.在pom文件中引入即可 <dependency> <groupId>o ...
- SpringBoot集成security
本文就SpringBoot集成Security的使用步骤做出解释说明.
- springboot集成Actuator
Actuator监控端点,主要用来监控与管理. 原生端点主要分为三大类:应用配置类.度量指标类.操作控制类. 应用配置类:获取应用程序中加载的配置.环境变量.自动化配置报告等与SpringBoot应用 ...
- SpringBoot集成Shiro并用MongoDB做Session存储
之前项目鉴权一直使用的Shiro,那是在Spring MVC里面使用的比较多,而且都是用XML来配置,用Shiro来做权限控制相对比较简单而且成熟,而且我一直都把Shiro的session放在mong ...
- SpringBoot集成redis的key,value序列化的相关问题
使用的是maven工程 springBoot集成redis默认使用的是注解,在官方文档中只需要2步; 1.在pom文件中引入即可 <dependency> <groupId>o ...
- springboot集成mybatis(二)
上篇文章<springboot集成mybatis(一)>介绍了SpringBoot集成MyBatis注解版.本文还是使用上篇中的案例,咱们换个姿势来一遍^_^ 二.MyBatis配置版(X ...
- springboot集成mybatis(一)
MyBatis简介 MyBatis本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation迁移到了google code,并且改名为MyB ...
- springboot集成redis(mybatis、分布式session)
安装Redis请参考:<CentOS快速安装Redis> 一.springboot集成redis并实现DB与缓存同步 1.添加redis及数据库相关依赖(pom.xml) <depe ...
随机推荐
- 努比亚Z7 mini刷机教程_recovery卡刷机教程
之前小编分享努比亚Z7 mini电话访问Root权限.recovery刷机教程. 所以对于朋友谁搞机整机的爱,左边是写第三方手机刷包.那么下面刷的家小编与您分享努比亚Z7 mini刷机教程手机. 一. ...
- 从入门机器学习的零单排:OctaveMatlab经常使用绘图知识
OctaveMatlab经常使用绘图知识 之前一段时间在coursera看了Andrew ng的机器学习的课程,感觉还不错,算是入门了.这次打算以该课程的作业为主线,对机器学习基本知识做一下总结.小弟 ...
- FastDFS是纯C语言实现,只支持Linux,适合以中小文件为载体的在线服务,还可以冗余备份和负载均衡
一.理论基础 FastDFS比较适合以中小文件为载体的在线服务,比如跟NGINX(APACHE)配合搭建图片服务器. 分布式文件系统FastDFS FastDFS是纯C语言实现,只支持Linux.Fr ...
- WPF使用矢量字体图标(阿里巴巴iconfont)
原文:WPF使用矢量字体图标(阿里巴巴iconfont) 版权声明:本文为博主原创文章,转载请注明出处. https://blog.csdn.net/lwwl12/article/details/78 ...
- POJ读书笔记2.1 —— 鸡兔笼带
http://blog.csdn.net/pipisorry/article/details/36433305 问题描写叙述 一个笼子里面关了鸡和兔子(鸡有2仅仅脚.兔子有4仅仅脚.没有例外). 已 ...
- 手把手教你开发Nginx模块
前面的哪些话 关于Nginx模块开发的博客资料,网上很多,很多.但是,每篇博客都只提要点,无法"step by step"照着做,对于初次接触Nginx开发的同学,只能像只盲目的蚂 ...
- 通过 vuex 实现 vue-echarts 图表的手动 resize
背景:项目有用到 vue-echarts, 百度推出的 vue 版本的 Echarts,图表自带响应式属性 auto-resize, 来实现窗口尺寸变化时,图表的尺寸自适应,但是发现它是靠监听 win ...
- Delphi中close与Terminate方法的区别
在有多个Form窗体时可以体现出来.用close是只关闭本窗体,而用Application.terminate是关闭整个程序,包括所有窗体.(1)当Close是一个主窗体时,程序会退出.Close会发 ...
- WinForm 清空界面控件值的小技巧
原文:WinForm 清空界面控件值的小技巧 在WinForm里面有时候需要清空自己输入内容或是选择的选项,以便重新操作流程,那么一般你是怎么清空界面各个控件值的呢?如果窗体里面控件,尤其是TextB ...
- 微信小程序把玩(十四)button组件
原文:微信小程序把玩(十四)button组件 button按钮用的算是最普遍的组件之一. 主要属性: wxml <!--按钮默认样式,点击事件--> <button type=&qu ...