JMockit
简介
JMockit是基于JavaSE5中的java.lang.instrument包开发,内部使用ASM库来动态修改java的字节码,使得java这种静态语言可以想动态脚本语言一样动态设置被Mock对象私有属性,模拟静态、私有方法行为等等website,参考这里;这里;
JMockit有两种Mock方式:基于行为的Mock方式和基于状态的Mock方式:
- (基于行为的)根据输入输出,功能测试,类似黑盒测试。需要把依赖的代码mock掉,实际相当于改变了被依赖的代码的逻辑。
- (基于状态的)根据用例测试路径,测试代码内部逻辑,接近白盒测试。目的是测试单元测试及其依赖代码的调用过程,验证代码逻辑是否满足测试路径。
测试解决方案
- Mock类的属性和行为(属性通过注入解决,行为通过对mock对象
@Mocked覆盖方法) - 手工注入(Deencapsulation.setField()方法)
- 单级注入(@Tested,@Injectable实现简单对象注入)
- 多级注入(需要mock对象,层层手工注入,单级注入不生效)
- Mock接口(覆盖率插件可能会抛错,Mock实现类,返回接口类型)
关键字:@Tested,@Injectable,Expectations类,@Mocked{result,times,invoke()}, MokeUp<T>
基于行为的Mock方式
1.录制方法预期行为。
2.真实调用。
3.验证录制的行为被调用。
public class GuessTest {
@Tested // 表明被修饰实例是将会被自动构建和注入的实例,自动Mock
Guess guess = new Guess(3);
@Injectable // 表明被修饰实例将会自动注入到@Tested修饰的实例中,并且会自动mock掉,除非在测试前被赋值
GuessDAO guessDAO;
/**
* 连续3次失败
*/
@Test
public void behaviorTest_fail3time() {
new Expectations() { // Expectations中包含的内部类区块中,体现的是一个录制被测类的逻辑。
@Mocked(methods="tryIt") // 表明被修饰的类对tryIt()方法进行mock。
Guess g;
{
g.tryIt(); // 期待调用Guess.tryIt()方法
result = false; // mock掉返回值为false(表明猜不中)
times = 3; // 期待以上过程重复3次
guessDAO.saveResult(false, anyInt); // 期待调用guessDAO把猜失败的结果保存
}
};
guess.doit(); // 录制完成后,进行实际的代码调用,也称回放(replay)
}
}
@Tested和@Injectable:
对@Tested对象判断是否为null,是则通过合适构造器初始化,并实现依赖注入。调用构造方法时,会尝试使用@Injectable的字段进行构造器注入。普通注入时,@Injectable字段如果没有在测试方法前被赋值,其行为将会被mock成默认值(静态方法和构造函数不会被mock掉)。Injectable最大作用除了注入,还有就是mock的范围只限当前注释实例。
总结为:@Injectable的实例会自动注入到@Tested中,如果没初始赋值,那么JMockit将会以相应规则初始化。
@Mocked:@Mocked修饰的实例,将会把实例对应类的所有实例的所有行为都mock掉(无论构造方法,还是private,protected方法,够霸气吧)。在Expectation区块中,声明的成员变量均默认带有@Mocked,但是本例没有省略,是因为@Mocked会mock掉所有方法,而回放的代码中doit函数我们是不希望它也被mock,所以通过method="tryIt"来设置被mock的类只对tryIt方法进行mock。
Expectations:这是录制期望发生行为的地方。result和times都是其内定成员变量。result可以重定义录制行为的返回值甚至通过Delegate来重定义行为,times是期待录制行为的发生次数。在Expectations中发生的调用,均会被mock。由于没定义result,所以guessDAO.saveResult()调用的结果返回空。
方法二(基于行为的)
@Test
public void behaviorTest_sucecess() {
new Expectations(Guess.class) { // 构造函数可以传入Class或Instance实例
{
guess.tryIt();
result = false;
times=2;
invoke(guess, "randomGuess", new Object[]{}); // invoke()能调用私有方法
result = (Integer)getField(guess, "number"); // getField()能操作私有成员
guessDAO.saveResult(true, (Integer)getField(guess, "number"));
}
};
guess.doit();
}
所以录制中会先调用2次tryIt并返回false,在发生第3次调用时,通过invoke调用私有方法randomGuess,并期待其返回被测实例的私有成员number,通过这种作弊的方式,自然肯定能在第三次猜中数字。最后期待guessDAO把结果保存。
这段代码和之前的区别是,在Expectation中没定义成员变量,而把Guess.class显式地通过构造函数传入。这么做也是为了只对tryIt方法mock,因为在Expectation构造函数传入Class对象或Instance对象后,只会区块内Class或Instance对应的行为进行mock。
方法三(基于状态的)
使用MockUp,@Mock。通过这种方式,改变方法内部逻辑,实现类似白盒测试的功能。MockUp中的泛型类型是被重定义的类,重定义的方法需要和原类中的方法签名一致。但是,static方法可以省去static关键字。如:
public class StateMocked {
public static int getDouble(int i){
return i*2;
}
}
@Test
public void testMockNormalMethodContent() throws IOException {
StateMocked obj = new StateMocked();
new MockUp<StateMocked>() {//使用MockUp修改被测试方法内部逻辑
@Mock
public int getTriple(int i) {
return i * 30;
}
};
assertEquals(30, obj.getTriple(1));
assertEquals(60, obj.getTriple(2));
}
JMockit的更多相关文章
- Jmockit使用
引用单元测试中mock的使用及mock神器jmockit实践中的java单元测试中各种Mock框架对比,就能明白JMockit有多么强大: JMockit是基于JavaSE5中的java.lang.i ...
- 使用JUnit4与JMockit进行打桩测试
1. 何为Mock 项目中各个模块,各个类之间会有互相依赖的关系,在单元测试中,我们只关心被测试的单元,对于其依赖的单元并不关心(会有另外针对该单元的测试). 比如,逻辑层A类依赖了数据访问层B类的取 ...
- JMockit使用总结
Jmockit可以做什么 使用JMockit API来mock被依赖的代码,从而进行隔离测试. 类级别整体mock和部分方法重写 实例级别整体mock和部分mock mock静态方法.私有变量.局部方 ...
- jmockit学习
下图为jmockit 类图.在我们编写代码时几乎都会用到Expectations(期望)和Verifications(校验),二者均继承自Invacations. 常会用到的注解有:@Mocked @ ...
- jmockit学习总结
mock类型和实例 从依赖的测试代码调用的方法和构造函数是mock(模拟)的目标. Mocking提供了我们需要的机制,以便将被测试的代码与(一些)依赖关系隔离开来.我们通过声明适当的模拟字段和/或模 ...
- Jmockit之mock特性详解
本文是Jmockit学习过程中,根据官网所列的工具特性进行解读. 1.调用次数约束(Invocation count constraints) 可以通过调用计数约束来指定预期和/或允许匹配给定期望的调 ...
- 单元测试系列:Mock工具Jmockit使用介绍
更多原创测试技术文章同步更新到微信公众号 :三国测,敬请扫码关注个人的微信号,感谢! 原文链接:http://www.cnblogs.com/zishi/p/6760272.html Mock工具Jm ...
- JMockit常用操作
JMockit常用操作 2017-11-30 转自:http://blog.csdn.net/foreverling/article/details/51234149 目录 1 基本概念 1.1 常 ...
- 单元测试系列之十一:Jmockit之mock特性详解
本文是Jmockit学习过程中,根据官网所列的工具特性进行解读. 1.调用次数约束(Invocation count constraints) 可以通过调用计数约束来指定预期和/或允许匹配给定期望的调 ...
随机推荐
- C++中的结构体
http://zhidao.baidu.com/link?url=8OYQSKV9mvSBc6Hkf9NsLQmipSge9VCZDJQGAZZs5PCBQ54UTmK98VRmAklEEAFYu7d ...
- Apache2.4.6 添加虚拟主机
apache2.4 与 apache2.2 的虚拟主机配置写法有所不同 apache2.2的写法: <VirtualHost *:80> ServerName domain.com Doc ...
- 10. javacript高级程序设计-DOM
1. DOM DOM(文档对象模型)是针对HTML和XML文档的一个API(应用程序接口) 1.1 节点层次 DOM可以将任何HTML和XML文档描绘成一个由多层节点构成的结构.节点分为几种不同的类型 ...
- 【干货】ECS服务器OPENVPN搭建,方便管理所有内网服务器
[干货]ECS服务器OPENVPN搭建,方便管理所有内网服务器 使用场景 一台有外网的ECS服务器+N台无外网的ECS服务器,使用OPENVPN管理全部的ECS服务器(包括无外网的ECS服务器). 鉴 ...
- mybatis 获取自增ID
在Mybatis Mapper文件中添加属性“useGeneratedKeys”和“keyProperty”,其中keyProperty是Java对象的属性名,而不是表格的字段名. <inser ...
- App主界面Tab实现方法
ViewPager + FragmentPagerAdapter 这里模仿下微信APP界面的实现 国际惯例,先看下效果图: activity_main.xml 布局文件: <?xml ver ...
- IOS - SDWebImage 非ARC 问题
非arc项目中使用SDWebImage类库 1.添加类库引用 (1)ImageIO.framework (2)MapKit.framework 2.在targets->build P ...
- .net在当前日期的基础上加一天
比如今天是:2015-11-10 18:57:01,在这个基础上加一天,那么就是2015-11-11 18:57:01,代码如下: DateTime now_dt = DateTime.Now; ). ...
- asp.net 防止按钮重复提交
1.将按钮属性设置如下: <asp:Button ID="btConfirm" runat="server" Text="Confirm&quo ...
- vs c# int & int32
在vs c#中,int就等价于int32, 所以通常也是使用int32 当在统计总数时,最好使用int32,int16数值范围太小,如果超出,就会变成随机数.