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 ...
随机推荐
- JQUERY名称冲突
jQuery 使用 $ 作为符号 jQuery 介绍的简单方法. 其他 JavaScript 库函数(例 Prototype)使用相同的 $ 符号. jQuery 使用命名 noConflict() ...
- WPF 动态模拟CPU 使用率曲线图
原文:WPF 动态模拟CPU 使用率曲线图 在工作中经常会遇到需要将一组数据绘制成曲线图的情况,最简单的方法是将数据导入Excel,然后使用绘图功能手动生成曲线图.但是如果基础数据频繁更改, ...
- jquery权限选择
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...
- JS如何为iframe添加onclick事件
如果页面上有iframe时,鼠标点击在iframe内时,包含iframe的document是不响应任何事件的, 例如: $("#iframe1").click(function() ...
- requirejs教程(一):基本用法
介绍 RequireJS是一个非常小巧的JavaScript模块载入框架,是AMD规范最好的实现者之一.最新版本的RequireJS压缩后只有14K,堪称非常轻量.它还同时可以和其他的框架协同工作,使 ...
- ELINK编程器支持芯片详细列表
支持MCU芯片包括:STM32 F0.F1.F2.F3.F4.L0.L1全系列: GD32 F10XX系列. 各系列芯片支持详情如下:
- Pytorch Code积累
2017 Python最新面试题及答案16道题 15个重要Python面试题 测测你适不适合做Python? torch.squeeze() Returns a tensor with all the ...
- 腾讯QQ 8.9.3体验版发布 在线文档多端同步实时保存
感谢N软网的投递 腾讯体验中心迎来QQ8.9.3首个维护体验版发布,详细版本号为v8.9.3.21006,上一个体验版v8.9.2.20717发布于4月20日,时隔34天又迎来了更新.本次升级主要是在 ...
- UWP 圆角TextBox和PassWord框
最近在做一个UWP项目,登录的用户和密码框需要圆角的,由于UWP的TextBlock 和PasswordBox是没有CornerRadius属性的,于是我就使用了一个Border嵌套在最外层,设置其他 ...
- [机器学习]Generalized Linear Model
最近一直在回顾linear regression model和logistic regression model,但对其中的一些问题都很疑惑不解,知道我看到广义线性模型即Generalized Lin ...