公司对开发人员的单元测试要求比较高,要求分支覆盖率、行覆盖率等要达到60%以上等等。项目中已经集成了jmockit这个功能强大的mock框架,学会使用这个框架势在必行。从第一次写一点不会,到完全可以应付工作要求,期间踩了好多坑,学到了不少东西。下面简单总结一下jmockit这个框架的使用,重点介绍MockUp的使用,因为项目中都采用此种方式模拟方法。

一、框架集成

添加maven依赖

 <dependencies>
<!-- jmockit必须写在junit之前 -->
<dependency>
<groupId>org.jmockit</groupId>
<artifactId>jmockit</artifactId>
<version>1.16</version>
<scope>test</scope>
</dependency> <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency> </dependencies>

二、@Mocked模拟方式介绍

@Mocked模拟,由录制、回放、验证三步骤完成,是对某个类的所有实例的所有方法进行完整的模拟方式。

/**
* 被测试类
*/
public class App { public String say() {
return "Hello World";
} public String say2(){
return "Hello World 2";
} public static String staticSay() {
return "Still hello world";
}
}
/**
* 测试类
*/
public class AppTest { /**
* 针对类及所有实例的的整体模拟,未写录制的方法默认返回0,null等
*/
@Mocked
App app; @Test
public void testSay() { //录制,定义被模拟的方法的返回值,可以录制多个行为,写在一个大括号里也可以,多个大括号隔开也可以
new Expectations() {{
app.say();
result = "say";
}}; //回放,调用模拟的方法
System.out.println(app.say()); //say
System.out.println(new App().say()); //say
System.out.println(App.staticSay()); //null //验证
new Verifications() {{
//验证say模拟方法被调用,且调用了2次
app.say();
times = 2; //验证staticSay模拟方法被调用,且调用了1次
App.staticSay();
times = 1;
}}; }
}

三、@Injectable模拟方式介绍

@Injectable和@Mocked的方式很像,区别是@Injectable仅仅对当前实例进行模拟。 

/**
* 测试类
*/
public class AppTest001 { /**
* 仅针对当前实例的整体模拟
*/
@Injectable
App app; @Test
public void testSay() { //录制
new Expectations() {{
app.say();
result = "say";
}}; //回放
System.out.println(app.say()); //say,模拟值
System.out.println(app.say2()); //null,模拟默认值 final App appNew = new App();
System.out.println(appNew.say()); //Hello World,未被模拟,方法实际值
System.out.println(App.staticSay()); //Still hello world,未被模拟,方法实际值 //验证
new Verifications() {
{
//验证say模拟方法被调用
app.say();
times = 1;
}
{
appNew.say();
times = 1;
}
{
//验证staticSay模拟方法被调用,且调用了1次
App.staticSay();
times = 1;
}
}; }
}

四、Expectations传参,局部模拟

/**
* 测试类
*/
public class AppTest002 { @Test
public void testSay() { final App app = new App(); //录制,带参数表示局部模拟【针对所有实例的局部模拟,具体到某个方法的模拟,其它方法不模拟】
new Expectations(App.class) {{
app.say();
result = "say";
}}; //回放
System.out.println(app.say()); //say,模拟值
System.out.println(app.say2()); //Hello World 2 ,未被模拟,方法实际值
System.out.println(new App().say()); //say,模拟值
System.out.println(App.staticSay()); //Still hello world,未被模拟,方法实际值 }
}

五、MockUp局部模拟,可重写原有方法的逻辑,比较灵活,推荐使用

/**
* 测试类
*/
public class AppTest003 { @Test
public void testSay() {
//局部模拟【针对所有实例的局部模拟,具体到某个方法的模拟,其它方法不模拟】
new MockUp<App>(App.class){ @Mock
String say(){
return "say";
}
}; //回放
System.out.println(new App().say()); //say,模拟值
System.out.println(new App().say2()); //Hello World 2,未被模拟,方法实际值
System.out.println(App.staticSay()); //Still hello world,未被模拟,方法实际值 }
}

六、MockUp如何模拟私有方法、静态方法、静态块、构造函数等

1.模拟私有属性(实例属性和类属性),MockUp不支持,采用如下方式

//模拟实例的字段
Deencapsulation.setField(Object objectWithField, String fieldName, Object fieldValue) //模拟类的静态字段
Deencapsulation.setField(Class<?> classWithStaticField, String fieldName, Object fieldValue)

2.模拟私有方法,MockUp不支持,采用如下方式

//模拟实例方法,注意参数不能为null,如果要传null请使用带参数类型的另一个重载方法
Deencapsulation.invoke(Object objectWithMethod, String methodName, Object... nonNullArgs) //模拟类方法
Deencapsulation.invoke(Class<?> classWithStaticMethod, String methodName, Object... nonNullArgs)

3.模拟静态方法

和模拟实例方法一样,去掉static即可

4.模拟静态块

 //mock静态代码块
@Mock
void $clinit(Invocation invocation){ }

5.模拟实例块和构造函数

//mock代码块和构造函数
@Mock
void $init(Invocation invocation) { }

6.模拟接口,MockUp不支持,采用@Capturing

/**
* 被模拟的接口
*/
public interface IUserService { String getUserName( );
}
public class UserServiceImpl implements IUserService {

    @Override
public String getUserName() {
return "Bob";
}
}
/**
* 接口模拟测试
*/
public class IUserServiceTest { /**
* MockUp不能mock接口方法,可以用来生成接口实例
*/
@Test
public void getUserNameTest001(){
MockUp<IUserService> mockUp = new MockUp<IUserService>(){ @Mock
String getUserName( ){
return "Jack";
}
}; IUserService obj = new UserServiceImpl();
System.out.println(obj.getUserName()); //Bob,mock失败 obj = mockUp.getMockInstance();
System.out.println(obj.getUserName()); //Jack,mockUp生成的实例,和自己写一个接口实现一样 obj = new UserServiceImpl();
System.out.println(obj.getUserName()); //Bob,mock失败
} /**
* @Capturing 注解可以实现mock接口,所有实现类的实例均被mock
* @param base
*/
@Test
public void getUserNameTest002(@Capturing final IUserService base){
IUserService obj = new UserServiceImpl();
System.out.println(obj.getUserName()); //mock成功,返回模拟默认值null //录制
new Expectations(){
{
base.getUserName();
result = "Jack";
}
};
System.out.println(obj.getUserName()); //Jack obj = new IUserService() {
@Override
public String getUserName() {
return "Alice";
}
};
System.out.println(obj.getUserName()); //Jack
} }

七、MockUp模拟方法中调用原方法

/**
* 模拟方法调用原方法逻辑测试
*/
public class JSONObjectTest { @Test
public void getTest(){
JSONObject jsonObject = new JSONObject();
jsonObject.put("a","A");
jsonObject.put("b","B");
jsonObject.put("c","C");
System.out.println(jsonObject.get("a")); //A
System.out.println(jsonObject.get("b")); //B
System.out.println(jsonObject.get("c")); //C
new MockUp<JSONObject>(){
@Mock
Object get(Invocation invocation,Object key){
if("a".equals(key)){
return "aa";
}else{
//调用原逻辑
return invocation.proceed(key);
}
}
};
System.out.println(jsonObject.get("a")); //aa
System.out.println(jsonObject.get("b")); //B
System.out.println(jsonObject.get("c")); //C
}
}

八、MockUp单元测试用例单个跑正常,批量跑失败可能的原因

1.测试方法使用了共享变量,相互影响。

2.在一个测试方法里多次MockUp同一个类,将某个类需要mock的方法均写在一个new MockUp里即可解决,原因未知。

3.在测试方法里的MockUp,在方法结束前,调用一下mockUp.tearDown。

jmockit使用总结-MockUp重点介绍的更多相关文章

  1. 消息队列介绍、RabbitMQ&Redis的重点介绍与简单应用

    消息队列介绍.RabbitMQ&Redis的重点介绍与简单应用 消息队列介绍.RabbitMQ.Redis 一.什么是消息队列 这个概念我们百度Google能查到一大堆文章,所以我就通俗的讲下 ...

  2. .NET:“事务、并发、并发问题、事务隔离级别、锁”小议,重点介绍:“事务隔离级别"如何影响 “锁”?

    备注 我们知道事务的重要性,我们同样知道系统会出现并发,而且,一直在准求高并发,但是多数新手(包括我自己)经常忽略并发问题(更新丢失.脏读.不可重复读.幻读),如何应对并发问题呢?和线程并发控制一样, ...

  3. 各种vpn协议介绍(重点介绍sslvpn的实现方式openvpn)

    vpn介绍:   VIrtual Private Network 虚拟专用网络哪些用户会用vpn?    公司的远程用户(出差.家里),公司的分支机构.idc机房.企业间.FQ常见vpn协议有哪些?  ...

  4. spring框架总结(03)重点介绍(Spring框架的第二种核心掌握)

    1.Spring的AOP编程 什么是AOP?  ----- 在软件行业AOP为Aspect Oriented Programming  也就是面向切面编程,使用AOP编程的好处就是:在不修改源代码的情 ...

  5. 进击的Python【第十一章】:消息队列介绍、RabbitMQ&Redis的重点介绍与简单应用

    消息队列介绍.RabbitMQ.Redis 一.什么是消息队列 这个概念我们百度Google能查到一大堆文章,所以我就通俗的讲下消息队列的基本思路. 还记得原来写过Queue的文章,不管是线程queu ...

  6. java中关于IO流的知识总结(重点介绍文件流的使用)

    今天做软件构造实验一的时候,用到了java文件流的使用,因为之前学的不是很踏实,于是决定今天好好总结一下, 也方便以后的回顾. 首先,要分清IO流中的一些基础划分: 按照数据流的方向不同可以分为:输入 ...

  7. html的特质语义:微格式及其他(重点介绍其中两种)

    今天再次翻开html的书本, 感觉过了个周末似乎生疏了许多, 虽然我是刚接触html的, 但是对于他还是抱有极其大的兴趣的, 所以不爱看书的我, 也开始一遍遍的翻阅着书本, 寻找解决问题的方法, 下面 ...

  8. ISO c++ 14 重点介绍[译]

    原文链接 http://marknelson.us/2014/09/11/highlights-of-iso-c14/ 下面是对你的日常开发有重大影响的C++14新变动,列出了一些示例代码,并讨论何时 ...

  9. 谈谈javascript数组排序方法sort()的使用,重点介绍参数使用及内部机制?

    语法:arrayObject.sort(sortby) 参数sortby可选,规定排序顺序,必须是函数: 注:如果调用该方法时没有使用参数,将按字符编码的顺序进行排序,要实现这一点,首先应把数组的元素 ...

随机推荐

  1. 《MVC架构下网站的设计与实现》论文笔记(十八)

    标题:MVC架构下网站的设计与实现 一.基本信息 时间:2017 来源:广东海洋大学数学与计算机学院 关键词:网站设计:MVC 框架:数据库:网络安全 二.研究内容 1.系统的整体架构设计(以广东海洋 ...

  2. 项目Beta冲刺(团队)——用户试用调查报告

    项目Beta冲刺(团队)--用户试用调查报告 格式描述 课程名称:软件工程1916|W(福州大学) 作业要求:项目Beta冲刺(团队) 团队名称:为了交项目干杯 作业目标:针对相应版本展开用户试用,完 ...

  3. NameNode的HA

    HDFS中的NameNode的HA怎么实现?(一言以蔽之) 在Hadoop集群中配置并启动两个NameNode进程,一个作为Active节点对外提供服务,另一个作为Standby的节点,两个NameN ...

  4. js的对象创建

    创建对象 字面量的方式: var myHonda = {color: "red", wheels: 4, engine: {cylinders: 4, size: 2.2}} 就是 ...

  5. 什么叫pure function(纯函数)

    (来自:http://en.wikipedia.org/wiki/Pure_function) 在计算机编程中,假如满足下面这两个句子的约束,一个函数可能被描述为一个纯函数: 给出同样的参数值,该函数 ...

  6. Codeforces 1038 D. Slime

    [传送门] 其实就是这些数字前面能加正负号,在满足正负号均出现的情况下价值最大.那么就可以无脑DP$f[i][j][k]$表示到了第$i$位,正号是否出现($j$.$k$为$0$或$1$)能得到的最大 ...

  7. vim命令(转)

    1.Linux下创建文件 vi test.txt 或者 vim test.txt 或者 touch test.txt 2.vi/vim 使用 基本上 vi/vim 共分为三种模式,分别是命令模式(Co ...

  8. presto-gateway nodejs client

    目前已经有了好几个presto nodejs 的client,为了方便presto-gateway 的连接,修改了一个现有的nodejs client 可以方便的连接presto-gateway 原理 ...

  9. A*G/C011

    A*G/C011 A Airport Bus 不会zbl/kk B Colorful Creatures 枚举每个开始的点直接倍增 我好像sb了,可行的是一段前缀所以可以直接2分 C Squared ...

  10. cgdsR 下载TCGA数据

    TCGA 的数据可以在5个组织机构获取,它们都提供了类似的接口来供用户下载数据. cgdsR 包是cBioPortal 提供的R包 http://www.cbioportal.org/rmatlab ...