`

[转]使用JUnit4与JMockit进行打桩测试

阅读更多

1. 何为Mock

项目中各个模块,各个类之间会有互相依赖的关系,在单元测试中,我们只关心被测试的单元,对于其依赖的单元并不关心(会有另外针对该单元的测试)。

比如,逻辑层A类依赖了数据访问层B类的取数方法,然后进行逻辑处理。在对A的单元测试中,我们关注的是在B返回不同的查询结果的时候,A是怎么处理的,而不是B到底是怎么取的数,如何封装成一个模型等等。

因此,要屏蔽掉这些外部依赖,而Mock让我们有了一套仿真的环境。

目前业界有几种Mock,这里选用最全面的JMockit进行总结。

2. JMockit简介

JMockit的工作原理是通过asm修改原有class的字节码,再利用jdk的instrument机制替换现有class的内容,从而达到mock的目的。

这里使用的JMockit是1.21版本,具体使用方法可能与其他版本的不一样,但思想是相通的。Maven 配置如下:

<dependency>
  <groupId>org.jmockit</groupId> 
  <artifactId>jmockit</artifactId> 
  <version>1.21</version>
  <scope>test</scope>
</dependency>

JMockit有两种测试方式,一种是基于行为的,一种是基于状态的测试。

1) Behavior-oriented(Expectations & Verifications)  

2)State-oriented(MockUp<GenericType>)   

通俗点讲,Behavior-oriented是基于行为的mock,对mock目标代码的行为进行模仿,更像黑盒测试。State-oriented 是基于状态的mock,是站在目标测试代码内部的。可以对传入的参数进行检查、匹配,才返回某些结果,类似白盒。而State-oriented的 new MockUp基本上可以mock任何代码或逻辑。

假设现在有两个类,Service和DAO.  Service通过数据库查询出不同分组货物的数量,得到货物是否畅销。

复制代码
 1 package com.khlin.test.junit.jmockit.demo;
 2 
 3 public class Service {
 4     
 5     private DAO dao;
 6     
 7     public void setDao(DAO dao) {
 8         this.dao = dao;
 9     }
10     
11     /**
12      * 根据存货量判断货物是否畅销
13      * @param group
14      * @return
15      */
16     public Status checkStatus(String group) {
17         int count = this.dao.getStoreCount(group);
18 
19         if (count <= 0) {
20             return Status.UNKOWN;
21         } else if (count <= 800) {
22             return Status.UNSALABLE;
23         } else if (count <= 1000) {
24             return Status.NORMAL;
25         } else {
26             return Status.SELLINGWELL;
27         }
28     }
29 }
复制代码
复制代码
 1 package com.khlin.test.junit.jmockit.demo;
 2 
 3 import java.util.HashMap;
 4 import java.util.Map;
 5 
 6 public class DAO {
 7 
 8     private Map<String, Integer> groupCounts = new HashMap<String, Integer>();
 9 
10     /**
11      * 假数据
12      */
13     {
14         this.groupCounts.put("A", 500);
15         this.groupCounts.put("B", 1000);
16         this.groupCounts.put("C", 1200);
17     }
18 
19     public int getStoreCount(String group) {
20         Integer count = this.groupCounts.get(group);
21 
22         return null == count ? -1 : count.intValue();
23     }
24 }
复制代码
复制代码
 1 package com.khlin.test.junit.jmockit.demo;
 2 
 3 public enum Status {
 4 
 5     /**
 6      * 畅销
 7      */
 8     SELLINGWELL,
 9     /**
10      * 一般
11      */
12     NORMAL,
13     /**
14      * 滞销
15      */
16     UNSALABLE,
17     
18     /**
19      * 状态未知
20      */
21     UNKOWN
22 }
复制代码

 

基于行为的Mock 测试,一共三个阶段:record、replay、verify。

1)record:在这个阶段,各种在实际执行中期望被调用的方法都会被录制。

2)repaly:在这个阶段,执行单元测试Case,原先在record 阶段被录制的调用都可能有机会被执行到。这里有“有可能”强调了并不是录制了就一定会严格执行。

3)verify:在这个阶段,断言测试的执行结果或者其他是否是原来期望的那样。

假设现在我只想测试Service,在存货量900件的情况下,是否能正确返回NORMAL的状态。那么,我并不关心传入DAO的到底是哪个分组,也不关心DAO怎么去数据库取数,我只想让DAO返回900,这样就可以测试Service了。

示例代码:

复制代码
 1 @RunWith(JMockit.class)
 2 public class ServiceBehavier {
 3 
 4     @Mocked
 5     DAO dao = new DAO();
 6 
 7     private Service service = new Service();
 8 
 9     @Test
10     public void test() {
11 
12         // 1. record 录制期望值
13         new NonStrictExpectations() {
14             {
15                 /**
16                  * 录制的方法
17                  */
18                 dao.getStoreCount(anyString);// mock这个方法,无论传入任何String类型的值,都返回同样的值,达到黑盒的效果
19                 /**
20                  * 预期结果,返回900
21                  */
22                 result = 900;
23                 /**
24                 times必须调用两次。在Expectations中,必须调用,否则会报错,因此不需要作校验。
25                 在NonStrictExpectations中不强制要求,但要进行verify验证.但似乎已经强制要求了
26                 此外还有maxTimes,minTimes
27                 */
28                 times = 1;
29             }
30         };
31         service.setDao(dao);
32 
33         // 2. replay 调用
34         Assert.assertEquals(Status.NORMAL, service.checkStatus("D"));
35 
36 //        Assert.assertEquals(Status.NORMAL, service.checkStatus("D"));
37 
38          //3.校验是否只调用了一次。如果上面注释的语句再调一次,且把录制的times改为2,那么在验证阶段将会报错。
39         new Verifications() {
40             {
41                 dao.getStoreCount(anyString);
42                 times = 1;
43             }
44         };
45 
46     }
47 }
复制代码

基于状态的Mock测试

通过MockUp类,直接改写了mock类的代码逻辑,有点类似白盒测试。

复制代码
 1 public class ServiceState {
 2 
 3     private DAO dao;
 4     
 5     private Service service;
 6 
 7     @Test
 8     public void test() {
 9         
10         //1. mock对象
11         MockUp<DAO> mockUp = new MockUp<DAO>() {
12 
13             @Mock
14             public int getStoreCount(String group) {
15                 return 2000;
16             }
17         };
18         
19         //2. 获取实例
20         dao = mockUp.getMockInstance();
21         service = new Service();
22         service.setDao(dao);
23         
24         //3.调用
25         Assert.assertEquals(Status.SELLINGWELL, service.checkStatus("FFF"));
26         
27         //4. 还原对象,避免测试方法之间互相影响。其实对一个实例来说没什么影响,对静态方法影响较大。旧版本的tearDown()方法是Mockit类的静态方法
28         mockUp.tearDown();
29     }
30 }
复制代码

3. JMockit mock各种类型或方法的示例代码

抽象类 

复制代码
 1 package com.khlin.test.junit.jmockit.demo.jmockit;
 2 
 3 public abstract class AbstractA {
 4     
 5     public abstract int getAbstractAnything();
 6 
 7     public int getAnything() {
 8         return 1;
 9     }
10 }
复制代码

接口类

1 package com.khlin.test.junit.jmockit.demo.jmockit;
2 
3 public interface InterfaceB {
4     
5     public int getAnything();
6 }

普通类

复制代码
 1 package com.khlin.test.junit.jmockit.demo.jmockit;
 2 
 3 public class ClassA {
 4     
 5     InterfaceB interfaceB;
 6     
 7     private int number;
 8     
 9     public void setInterfaceB(InterfaceB interfaceB) {
10         this.interfaceB = interfaceB;
11     }
12     
13     public int getAnything() {
14         return getAnythingPrivate();
15     }
16     
17     private int getAnythingPrivate() {
18         return 1;
19     }
20     
21     public int getNumber() {
22         return number;
23     }
24     
25     
26     
27     public static int getStaticAnything(){
28         return getStaticAnythingPrivate();
29     }
30     
31     private static int getStaticAnythingPrivate() {
32         return 1;
33     }
34     
35     public int getClassBAnything() {
36         return this.interfaceB.getAnything();
37     }
38 }
复制代码

接口实现类

复制代码
1 package com.khlin.test.junit.jmockit.demo.jmockit;
2 
3 public class ClassB implements InterfaceB {
4 
5     public int getAnything() {
6         return 10;
7     }
8     
9 }
复制代码

终极测试代码

复制代码
  1 package com.khlin.test.junit.jmockit.demo;
  2 
  3 import mockit.Deencapsulation;
  4 import mockit.Expectations;
  5 import mockit.Injectable;
  6 import mockit.Mock;
  7 import mockit.MockUp;
  8 import mockit.Mocked;
  9 import mockit.NonStrictExpectations;
 10 import mockit.Tested;
 11 import mockit.Verifications;
 12 import mockit.integration.junit4.JMockit;
 13 
 14 import org.junit.Assert;
 15 import org.junit.Test;
 16 import org.junit.runner.RunWith;
 17 
 18 import com.khlin.test.junit.jmockit.demo.jmockit.AbstractA;
 19 import com.khlin.test.junit.jmockit.demo.jmockit.ClassA;
 20 import com.khlin.test.junit.jmockit.demo.jmockit.ClassB;
 21 import com.khlin.test.junit.jmockit.demo.jmockit.InterfaceB;
 22 
 23 @RunWith(JMockit.class)
 24 public class JMockitTest {
 25 
 26     /**
 27      * mock私有方法
 28      */
 29     @Test
 30     public void testPrivateMethod() {
 31 
 32         final ClassA a = new ClassA();
 33         // 局部参数,把a传进去
 34         new NonStrictExpectations(a) {
 35             {
 36                 Deencapsulation.invoke(a, "getAnythingPrivate");
 37                 result = 100;
 38                 times = 1;
 39             }
 40         };
 41 
 42         Assert.assertEquals(100, a.getAnything());
 43 
 44         new Verifications() {
 45             {
 46                 Deencapsulation.invoke(a, "getAnythingPrivate");
 47                 times = 1;
 48             }
 49         };
 50     }
 51 
 52     /**
 53      * mock私有静态方法
 54      */
 55     @Test
 56     public void testPrivateStaticMethod() {
 57 
 58         new NonStrictExpectations(ClassA.class) {
 59             {
 60                 Deencapsulation
 61                         .invoke(ClassA.class, "getStaticAnythingPrivate");
 62                 result = 100;
 63                 times = 1;
 64             }
 65         };
 66 
 67         Assert.assertEquals(100, ClassA.getStaticAnything());
 68 
 69         new Verifications() {
 70             {
 71                 Deencapsulation
 72                         .invoke(ClassA.class, "getStaticAnythingPrivate");
 73                 times = 1;
 74             }
 75         };
 76 
 77     }
 78 
 79     /**
 80      * mock公有方法
 81      */
 82     @Test
 83     public void testPublicMethod() {
 84         final ClassA classA = new ClassA();
 85         new NonStrictExpectations(classA) {
 86             {
 87                 classA.getAnything();
 88                 result = 100;
 89                 times = 1;
 90             }
 91         };
 92 
 93         Assert.assertEquals(100, classA.getAnything());
 94 
 95         new Verifications() {
 96             {
 97                 classA.getAnything();
 98                 times = 1;
 99             }
100         };
101     }
102 
103     /**
104      * mock公有静态方法--基于行为
105      */
106     @Test
107     public void testPublicStaticMethod() {
108 
109         new NonStrictExpectations(ClassA.class) {
110             {
111                 ClassA.getStaticAnything();
112                 result = 100;
113                 times = 1;
114             }
115         };
116 
117         Assert.assertEquals(100, ClassA.getStaticAnything());
118 
119         new Verifications() {
120             {
121                 ClassA.getStaticAnything();
122                 times = 1;
123             }
124         };
125     }
126     
127     /**
128      * mock公有静态方法--基于状态
129      */
130     @Test
131     public void testPublicStaticMethodBaseOnStatus() {
132 
133         MockUp<ClassA> mockUp = new MockUp<ClassA>() {
134             @Mock
135             public int getStaticAnything() { //注意这里不用声明为static
136                 return 100;
137             }
138         };
139         
140         Assert.assertEquals(100, ClassA.getStaticAnything());
141     }
142     
143     /**
144      * mock接口
145      */
146     @Test
147     public void testInterface() {
148         
149         InterfaceB interfaceB = new MockUp<InterfaceB>() {
150             @Mock
151             public int getAnything() {
152                 return 100;
153             }
154         }.getMockInstance();
155         
156         
157         ClassA classA = new ClassA();
158         classA.setInterfaceB(interfaceB);
159         
160         Assert.assertEquals(100, classA.getClassBAnything());
161     }
162     
163     /**
164      * mock接口--基于状态
165      */
166     @Test
167     public void testInterfaceBasedOnStatus() {
168         final InterfaceB interfaceB = new ClassB();
169         
170         new NonStrictExpectations(interfaceB) {
171             {
172                 interfaceB.getAnything();
173                 result = 100;
174                 times = 1;
175             }
176         };
177         
178         ClassA classA = new ClassA();
179         classA.setInterfaceB(interfaceB);
180         
181         Assert.assertEquals(100, classA.getClassBAnything());
182         
183         new Verifications() {
184             {
185                 interfaceB.getAnything();
186                 times = 1;
187             }
188         };
189     }
190     
191     
192     
193     /**
194      * mock抽象类
195      */
196     @Test
197     public void testAbstract() {
198         AbstractA abstractA = new MockUp<AbstractA>() {
199             @Mock
200             public int getAbstractAnything(){
201                 return 100;
202             }
203             
204             @Mock
205             public int getAnything(){
206                 return 1000;
207             }
208         }.getMockInstance();
209         
210         Assert.assertEquals(100, abstractA.getAbstractAnything());
211         
212         Assert.assertEquals(1000, abstractA.getAnything());
213     }
214 }
复制代码

 

分享到:
评论

相关推荐

    网易招聘的scrapy spider

    网易招聘的scrapy spider

    ACM计算两整数相加的多语言代码示例与注解

    内容概要:提供了基于不同编程语言的一个简单的 ACN(以计算两个整数的和为例)源代码,涉及三种语言:C++, Java 和 Python。每个示例都包含了详细的步骤来演示用户输入是如何接收和进行运算以及结果显示出来的。 适用人群:编程初学者、有一定编程经验希望熟悉多种语言实现方式的技术爱好者。 使用场景及目标:帮助理解不同编程语言基本操作方法,包括输入获取、数据处理、输出结果展示及其语法差异等知识点的运用。 其他补充:由于例子较为简易,便于作为教学案例或是新手练习之用,亦可用于快速对比几种主流编程语言的基础应用形式和技术特点。

    java-ssm+vue健身房管理系统实现源码(项目源码-说明文档)

    会员管理页面提供给管理员的功能有:对会员信息的一个管理,不同会员看到的页面是不一样的,会员信息也是不同 健身房信息管理页面提供给管理员的功能有:查看已发布的健身房区域数据,修改健身房区域信息或更换健身房区域内容,健身房区域信息作废,即可删除。 项目关键技术 开发工具:IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7+ 后端技术:ssm 前端技术:Vue 关键技术:springboot、SSM、vue、MYSQL、MAVEN 数据库工具:Navicat、SQLyog

    51Proteus仿真LCD1602+定时计数器实现可调电子时钟的编程

    51Proteus仿真LCD1602+定时计数器实现可调电子时钟的编程

    【高创新】基于侏儒猫鼬优化算法DMO-Transformer-BiLSTM实现故障识别Matlab实现.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。 替换数据可以直接使用,注释清楚,适合新手

    Linux安装python3_脚本_linux_install_py3.zip

    Linux安装python3_脚本_linux_install_py3

    BWO-Catboost白鲸算法优化Catboost分类预测,优化前后对比(Matlab完整源码和数据)

    1.Matlab实现BWO-Catboost白鲸算法优化Catboost分类预测,优化前后对比,Matlab调用Python的Catboost库(完整源码和数据)。 2.输出对比图、混淆矩阵图、预测准确率,运行环境Matlab2023及以上,配置Python的Catboost库。兼容测试链接:https://ww2.mathworks.cn/support/requirements/python-compatibility.html 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。 5.作者介绍:机器学习之心,博客专家认证,机器学习领域创作者,2023博客之星TOP50,主做机器学习和深度学习时序、回归、分类、聚类和降维等程序设计和案例分析,文章底部有博主联系方式。从事Matlab、Python算法仿真工作8年,更多仿真源码、数据集定制私信

    4-3_Business_GREEN_2017_02-CL-20180524MTAX.potx

    微软演示材料

    【高创新】基于龙格库塔优化算法RUN-Transformer-BiLSTM实现故障识别Matlab实现.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。 替换数据可以直接使用,注释清楚,适合新手

    【高创新】基于人工蜂鸟优化算法AHA-Transformer-BiLSTM实现故障识别Matlab实现.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。 替换数据可以直接使用,注释清楚,适合新手

    java基于ssm+vue小说阅读平台系统源码

    1、开发环境:ssm框架;内含Mysql数据库;VUE技术 2、需要项目部署的可以私信 3、项目代码都经过严格调试,代码没有任何bug! 4、该资源包括项目的全部源码,下载可以直接使用! 5、本项目适合作为计算机、数学、电子信息等专业的课程设计、期末大作业和毕设项目,作为参考资料学习借鉴。 6、本资源作为“参考资料”如果需要实现其他功能,需要能看懂代码,并且热爱钻研,自行调试。

    人工智能产品经理最佳实践__第三部分系统架构篇_aipm-1.zip

    人工智能产品经理最佳实践__第三部分系统架构篇_aipm-1

    【高创新】基于能量谷优化算法EVO-Transformer-BiLSTM实现故障识别Matlab实现.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。 替换数据可以直接使用,注释清楚,适合新手

    Mentor Graphics EDA介绍(九)--Zuken Interconnect Solutions.docx

    Mentor Graphics EDA介绍(九)--Zuken Interconnect Solutions

    VB+ACCESS班级管理系统(源代码+可执行程序+论文+开题报告+外文翻译+答辩ppt).zip

    1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看rEADME.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看READmE.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。

    4-3_Business_GREEN_2017_03.potx

    微软演示材料

    VB+sql学生管理系统(源代码+系统).zip

    1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。

    java-ssm+vue大学生心理健康系统实现源码(项目源码-说明文档)

    在技术实现部分采用了Java作为开发后台的编程语言,客户端使用html技术,数据库选择MySQL。最后进行了代码的编写,并说明了实现流程。最终,通过软件测试来验证大学生心理健康平台的功能要求。 项目关键技术 开发工具:IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7+ 后端技术:ssm 前端技术:Vue 关键技术:springboot、SSM、vue、MYSQL、MAVEN 数据库工具:Navicat、SQLyog

    一键安装+开防火墙+改连接数+自启动守护_矿池代理,支持TCP和SSL协议,支持自

    一键安装+开防火墙+改连接数+自启动守护_矿池代理,支持TCP和SSL协议,支持自定义抽水,高性能高_MinerProxy303

Global site tag (gtag.js) - Google Analytics