无论是敏捷开发、持续交付,还是测试驱动开发(TDD)都把单元测试作为实现的基石。随着这些先进的编程开发模式日益深入人心,单元测试如今显得越来越重要了。在敏捷开发、持续交付中要求单元测试一定要快(不能访问实际的文件系统或数据库),而TDD经常会碰到协同模块尚未开发的情况,而mock技术正是解决这些问题的灵丹妙药。

mock技术的目的和作用是模拟一些在应用中不容易构造或者比较复杂的对象,从而把测试与测试边界以外的对象隔离开。

我们可以自己编写自定义的Mock对象实现mock技术,但是编写自定义的Mock对象需要额外的编码工作,同时也可能引入错误。现在实现mock技术的优秀开源框架有很多,本文对几个典型的mock测试框架作了简明介绍,希望对大家有所帮助。

1.EasyMock

EasyMock 是早期比较流行的MocK测试框架。它提供对接口的模拟,能够通过录制、回放、检查三步来完成大体的测试过程,可以验证方法的调用种类、次数、顺序,可以令 Mock 对象返回指定的值或抛出指定异常。通过 EasyMock,我们可以方便的构造 Mock 对象从而使单元测试顺利进行。

EasyMock 是采用 MIT license 的一个开源项目,可以在 Sourceforge 上下载到。(http://sourceforge.net/projects/easymock/files/EasyMock/)

如果使用maven也可以如下引入:

<dependency><groupId>org.easymock</groupId><artifactId>easymock</artifactId><version>3.1</version><scope>test</scope>
</dependency>

使用EasyMock大致可以划分为以下几个步骤:

①    使用 EasyMock 生成 Mock 对象;

②    录制 Mock 对象的预期行为和输出;

③    将 Mock 对象切换到 播放 状态;

④    调用 Mock 对象方法进行单元测试;

⑤    对 Mock 对象的行为进行验证。

现在用一个例子来简单呈现以上的步骤,假设有一个类需要被模拟的类如下:

public class Class1Mocked {public  String hello(String name){System.out.println("hello "+name);return "hello "+name;}public void show(){System.out.println("Class1Mocked.show()");}
}

  首先静态导入EasyMock的方法:

import static org.easymock.EasyMock.*;

例1.1 EasyMock第一个例子

@Test
public void testMockMethod() {Class1Mocked obj = createMock(Class1Mocked.class);①expect(obj.hello("z3")).andReturn("hello l4");②replay(obj);③String actual = obj.hello("z3");④assertEquals("hello l4", actual);verify(obj);⑤
}

在⑤验证阶段中,会严格验证mock对象是否按录制的行为如期发生(包括执行的顺序及次数)。

2.mockito

EasyMock之后流行的mock工具。相对EasyMock学习成本低,而且具有非常简洁的API,测试代码的可读性很高。

mockito可以在https://code.google.com/p/mockito/上下载,如果使用maven可以如下引入:

<dependency><groupId>org.mockito</groupId><artifactId>mockito-all</artifactId><version>1.9.5</version><scope>test</scope>
</dependency>

使用mockito大致可以划分为以下几个步骤:

①    使用 mockito 生成 Mock 对象;

②    定义(并非录制) Mock 对象的行为和输出(expectations部分);

③    调用 Mock 对象方法进行单元测试;

④    对 Mock 对象的行为进行验证。

现在用一个例子来简单呈现以上的步骤:

  首先静态导入mockito的方法:

import static org.mockito.Mockito.*;

例2.1 mockito第一个例子

@Test
public void testMockMethod() {Class1Mocked obj=mock(Class1Mocked.class);①when(obj.hello("z3")).thenReturn("hello l4");②String actual=obj.hello("z3");③assertEquals("hello l4",actual);verify(obj).hello("z3");④//verify(obj,times(1)).hello("z3"); //可以加参数验证次数
}

可以看到与EasyMock相比,少了切换到播放状态一步。这是很自然的,本来就不是录制而谈播放呢,而在验证阶段可以通过增加参数(time(int)、atLeastOnce()、atLeast(int)、never()等)来精确验证调用次数。

而如果要验证调用顺序可以如下控制:

例2.2 验证顺序

@Test
public void testMockMethodInOrder() {Class1Mocked objOther = mock(Class1Mocked.class);Class1Mocked objCn = mock(Class1Mocked.class);when(objOther.hello("z3")).thenReturn("hello l4");when(objCn.hello("z3")).thenReturn("hello 张三");String other = objOther.hello("z3");assertEquals("hello l4", other);String cn = objCn.hello("z3");assertEquals("hello 张三", cn);InOrder inOrder = inOrder(objOther, objCn); //此行并不决定顺序,下面的两行才开始验证顺序inOrder.verify(objOther).hello("z3");inOrder.verify(objCn).hello("z3");
}

在之前的介绍的模拟操作中,我们总是去模拟一整个类或者对象,对于没有使用 When().thenReturn()方法指定的函数,系统会返回各种类型的默认值(具体值可参考官方文档)。而局部模拟创建出来的模拟对象依然是原系统对象,虽然可以使用方法When().thenReturn()来指定某些具体方法的返回值,但是没有被用此函数修改过的函数依然按照系统原始类的方式来执行,下面对非局部模拟和局部模拟分别举例来说明:

例2.3 非局部模拟

@Test
public void testSkipExpect() {Class1Mocked obj = mock(Class1Mocked.class);assertEquals(null, obj.hello("z3"));obj.show();verify(obj).hello("z3");verify(obj).show();
}

上面的代码省略了expectations部分(即定义代码行为和输出),运行该测试可以看到hello方法默认返回null(show方法本来就是无返回值的),而且在控制台中两个方法都没有输出任何语句。

mockito的局部模拟有两种方式,一种是doCallRealMethod()方式,另一种是spy()方式。

例2.4 局部模拟doCallRealMethod ()方式

@Test
public void testCallRealMethod () {Class1Mocked obj = mock(Class1Mocked.class);doCallRealMethod().when(obj).hello("z3"); assertEquals("hello z3",obj.hello("z3"));assertEquals(null,obj.hello("l4"));obj.show();verify(obj).hello("z3");verify(obj).hello("l4");verify(obj).show();
}

运行这个测试会发现在执行hello("z3")时会执行原有的代码,而执行hello("l4")时则是返回默认值null且没有输出打印,执行show()同样没有输出打印。

例2.5 局部模拟spy()方式

@Test
public void testSpy() {Class1Mocked obj = spy(new Class1Mocked());doNothing().when(obj).show();assertEquals("hello z3",obj.hello("z3"));obj.show();verify(obj).hello("z3");verify(obj).show();
}

运行这个测试会发现在执行hello("z3")时会执行原有的代码,但是执行show()时在控制台中没有打印语句。

但值得注意的是在mockito的psy()方式模拟中expectations部分使用的语法不同,执行起来存在微妙的不同,如下:

例2.6 值得注意的“陷阱”

@Test
public void testSpy2() {Class1Mocked obj = spy(new Class1Mocked());when(obj.hello("z3")).thenReturn("hello l4");assertEquals("hello l4",obj.hello("z3"));verify(obj).hello("z3");
}

上面的代码虽然能顺利运行,但在控制台中输出了hello z3,说明实际的代码仍然执行了,只是mockito在最后替换了返回值。但下面的代码就不会执行实际的代码:

@Test
public void testSpy3() {Class1Mocked obj = spy(new Class1Mocked());doReturn("hello l4").when(obj).hello("z3");assertEquals("hello l4",obj.hello("z3"));verify(obj).hello("z3");
}

3.PowerMock

这个工具是在EasyMock和Mockito上扩展出来的,目的是为了解决EasyMock和Mockito不能解决的问题,比如对static, final, private方法均不能mock。其实测试架构设计良好的代码,一般并不需要这些功能,但如果是在已有项目上增加单元测试,老代码有问题且不能改时,就不得不使用这些功能了。

PowerMock 在扩展功能时完全采用和被扩展的框架相同的 API, 熟悉 PowerMock 所支持的模拟框架的开发者会发现 PowerMock 非常容易上手。PowerMock 的目的就是在当前已经被大家所熟悉的接口上通过添加极少的方法和注释来实现额外的功能。目前PowerMock 仅扩展了 EasyMock 和 mockito,需要和EasyMock或Mockito配合一起使用。

PowerMock可以在https://code.google.com/p/powermock/上下载,本文以PowerMock+mockito为例,使用maven的话,添加如下依赖即可,maven会自动引入mockito的包。

<dependency><groupId>org.powermock</groupId><artifactId>powermock-api-mockito</artifactId><version>1.5</version>
<scope>test</scope>
</dependency>
<dependency><groupId>org.powermock</groupId><artifactId>powermock-module-junit4</artifactId><version>1.5</version>
<scope>test</scope>
</dependency>

现在举例来说明PowerMock的使用,假设有一个类需要被模拟的类如下:

public class Class2Mocked {public static int getDouble(int i){return i*2;}public String getTripleString(int i){return multiply3(i)+"";}private int multiply3(int i){return i*3;}
}

首先静态导入PowerMock的方法:

import static org.powermock.api.mockito.PowerMockito.*;

然后在使用junit4的测试类上做如下声明:

@RunWith(PowerMockRunner.class)
@PrepareForTest( { Class2Mocked.class })

例3.1 模拟静态方法

@Test
public void testMockStaticMethod() {mockStatic(Class2Mocked.class);when(Class2Mocked.getDouble(1)).thenReturn(3);int actual = Class2Mocked.getDouble(1);assertEquals(3, actual);verifyStatic();Class2Mocked.getDouble(1);
}

PowerMockit的局域模拟使用方式和mockito类似(毕竟是扩展mockito),但强大之处在于可以模拟private方法,普通方法和final方法。模拟普通方法和final方法的方式与模拟private方法一模一样,现以模拟private方法为例。

例3.2 模拟私有方法(doCallRealMethod方式)

@Test
public void testMockPrivateMethod() throws Exception {Class2Mocked obj = mock(Class2Mocked.class);when(obj, "multiply3", 1).thenReturn(4);doCallRealMethod().when(obj).getTripleString(1);String actual = obj.getTripleString(1);assertEquals("4", actual);verifyPrivate(obj).invoke("multiply3", 1);
}

例3.3 模拟私有方法(spy方式)

@Test
public void testMockPrivateMethod2() throws Exception {Class2Mocked obj = spy(new Class2Mocked());when(obj, "multiply3", 1).thenReturn(4);String actual = obj.getTripleString(1);assertEquals("4", actual);verifyPrivate(obj).invoke("multiply3", 1);
}

除此之外,PowerMock也可以模拟构造方法,如下所示:

例3.4 模拟构造方法

@Test
public void testStructureWhenPathDoesntExist() throws Exception { final String directoryPath = "mocked path"; File directoryMock = mock(File.class); whenNew(File.class).withArguments(directoryPath).thenReturn(directoryMock); when(directoryMock.exists()).thenReturn(true); File file=new File(directoryPath);assertTrue(file.exists()); verifyNew(File.class).withArguments(directoryPath); verifyPrivate(directoryMock).invoke("exists");
}

4.Jmockit

JMockit 是一个轻量级的mock框架是用以帮助开发人员编写测试程序的一组工具和API,该项目完全基于 Java 5 SE 的 java.lang.instrument 包开发,内部使用 ASM 库来修改Java的Bytecode。

Jmockit功能和PowerMock类似,某些功能甚至更为强大,但个人感觉其代码的可读性并不强。

Jmockit可以在https://code.google.com/p/jmockit/上下载,使用maven的话添加如下依赖即可:

<dependency><groupId>com.googlecode.jmockit</groupId><artifactId>jmockit</artifactId><version>1.0</version><scope>test</scope>
</dependency>

Jmockit也可以分类为非局部模拟与局部模拟,区分在于Expectations块是否有参数,有参数的是局部模拟,反之是非局部模拟。而Expectations块一般由Expectations类和NonStrictExpectations类定义。用Expectations类定义的,则mock对象在运行时只能按照 Expectations块中定义的顺序依次调用方法,不能多调用也不能少调用,所以可以省略掉Verifications块;而用NonStrictExpectations类定义的,则没有这些限制,所以如果需要验证,则要添加Verifications块。

现在举例说明Jmockit的用法:

例4.1 非局部模拟Expectations类定义

@Mocked  //用@Mocked标注的对象,不需要赋值,jmockit自动mock
Class1Mocked obj;@Test
public void testMockNormalMethod1() {new Expectations() {{obj.hello("z3");returns("hello l4", "hello w5");obj.hello("张三");result="hello 李四";}};assertEquals("hello l4", obj.hello("z3"));assertEquals("hello w5", obj.hello("z3"));assertEquals("hello 李四", obj.hello("张三"));try {obj.hello("z3");} catch (Throwable e) {System.out.println("第三次调用hello(\"z3\")会抛出异常");}try {obj.show();} catch (Throwable e) {System.out.println("调用没有在Expectations块中定义的方法show()会抛出异常");}
}

例4.2 非局部模拟 NonStrictExpectations类定义

public void testMockNormalMethod2() {new NonStrictExpectations() {{obj.hello("z3");returns("hello l4", "hello w5");}};assertEquals("hello l4", obj.hello("z3"));assertEquals("hello w5", obj.hello("z3"));assertEquals("hello w5", obj.hello("z3"));// 会返回在NonStrictExpectations块中定义的最后一个返回值
    obj.show();new Verifications() {{obj.hello("z3");times = 3;obj.show();times = 1;}};
}

运行这个测试会发现show()方法没有在控制台输出打印语句,说明是Jmockit对show方法也进行了默认mock。

例4.3 局部模拟

@Test
public void testMockNormalMethod() throws IOException {final Class1Mocked obj = new Class1Mocked();//也可以不用@Mocked标注,但需要final关键字new NonStrictExpectations(obj) {{obj.hello("z3");result = "hello l4";}};assertEquals("hello l4", obj.hello("z3"));assertEquals("hello 张三", obj.hello("张三"));new Verifications() {{obj.hello("z3");times = 1;obj.hello("张三");times = 1;}};
}

运行这个测试发现hello("z3")返回由Expectations块定义的值,但hello("张三")执行的是实际的代码。

例4.4 模拟静态方法

@Test
public void testMockStaticMethod() {new NonStrictExpectations(Class2Mocked.class) {{Class2Mocked.getDouble(1);result = 3;}};assertEquals(3, Class2Mocked.getDouble(1));new Verifications() {{Class2Mocked.getDouble(1);times = 1;}};
}

例4.5 模拟私有方法

@Test
public void testMockPrivateMethod() throws Exception {final Class2Mocked obj = new Class2Mocked();new NonStrictExpectations(obj) {{this.invoke(obj, "multiply3", 1);result = 4;}};String actual = obj.getTripleString(1);assertEquals("4", actual);new Verifications() {{this.invoke(obj, "multiply3", 1);times = 1;}};
}

例4.6 设置私有属性的值

假设有一个类需要被模拟的类如下:

public class Class3Mocked {private String name = "name_init";public String getName() {return name;}private static String className="Class3Mocked_init";public static String getClassName(){return className;}public static int getDouble(int i){return i*2;}public int getTriple(int i){return i*3;}
}

如下可以设置私有属性的值:

@Test
public void testMockPrivateProperty() throws IOException {final Class3Mocked obj = new Class3Mocked();new NonStrictExpectations(obj) {{this.setField(obj, "name", "name has bean change!");}};assertEquals("name has bean change!", obj.getName());
}

例4.7 设置静态私有属性的值

@Test
public void testMockPrivateStaticProperty() throws IOException {new NonStrictExpectations(Class3Mocked.class) {{this.setField(Class3Mocked.class, "className", "className has bean change!");}};assertEquals("className has bean change!", Class3Mocked.getClassName());
}

例4.8 改写普通方法的内容

@Test
public void testMockNormalMethodContent() throws IOException {final Class3Mocked obj = new Class3Mocked();new NonStrictExpectations(obj) {{new MockUp<Class3Mocked>() {@Mockpublic int getTriple(int i) {return i * 30;}};}};assertEquals(30, obj.getTriple(1));assertEquals(60, obj.getTriple(2));
}

例4.9 改写静态方法的内容

如果要改写Class3Mocked类的静态getDouble方法,则需要新建一个类含有与getDouble方法相同的函数声明,并且用@Mock标注,如下:

public class Class4Mocked {@Mockpublic static int getDouble(int i){return i*20;}
}

如下即可改写:

@Test
public void testDynamicMockStaticMethodContent() throws IOException {Mockit.setUpMock(Class3Mocked.class, Class4Mocked.class);assertEquals(20, Class3Mocked.getDouble(1));assertEquals(40, Class3Mocked.getDouble(2));
}

from: http://www.cnblogs.com/huangbin/archive/2013/04/27/3047671.html

java的mock测试框架相关推荐

  1. java流行的测试框架调研+市面上书籍调研

    主要是根据51job的右侧来判断 java测试框架名称 51job职位数量 百度词条数量 JUnit 732 48,500,000 REST Assured 3 19,500,000 Selenium ...

  2. java contains_Java常用测试框架大全,不要只会Junit

    介绍 要做好测试驱动开发,意识习惯最重要,其次也得回编写优雅的单元测试.java测试框架很多,各种断言和注解,有点混乱,编写测试时老师搞不清该用哪个. 本文简单梳理java常用的几个测试工具:Juni ...

  3. java junit mock_使用Mockito进行Java的Mock测试

    测试替身 dummy用于传递,不会真正使用,例如用于填充的方法的参数列表. Fake有简单实现,但通常被简化,比如在内存数据库,而不是真正的数据库中使用. Stub是接口或类中部分实现,测试时使用其实 ...

  4. 一文了解十大 Java 开发者必备测试框架!

    点击上方"CSDN",选择"置顶公众号" 关键时刻,第一时间送达! 想要提升自己的自动化测试技能吗?本文将介绍10个优秀的 Java 测试框架和库,来提升你的自 ...

  5. Java的Mock测试到底怎么使用

    Java的Mockito测试如何使用 由于种种原因,我们暂时无法从数据库.或者依赖其他系统的接口中,无法获取到测试数据,我们难以对自己的接口顺利进行测试,所以我们可以使用Mockito测试框架来Moc ...

  6. mockito mock测试框架

    1.简介 mock,[mɒk],adj. 虚拟的,模拟的. 如果你的代码对另一个类或者接口有依赖,mock测试能够帮你模拟这些依赖,从而完成测试. 使用场景: 类A有一个方法fun(B b),它依赖于 ...

  7. Java之Junit测试框架

    一.Junit 是什么? JUnit是一个Java语言的单元测试框架.它由Kent Beck和Erich Gamma建立,逐渐成为源于Kent Beck的sUnit的xUnit家族中最为成功的一个JU ...

  8. Java Karate BDD测试框架在POST接口中的应用

    应用场景:在自动化的测试中,测试数据是如何被应用的直接影响测试脚本运行的结果.而且更致命的是一味地应用"静态数据",自动化测试就像断了的链条. 学习要点: 1.如何动态更改post ...

  9. java移动端接口测试_走进Java接口测试之测试框架TestNG数据驱动(入门篇)

    前言 我们在前面的文章中,和大家分享过接口自动化测试一些基本的实现方法,但是,你很快就会发现,如果在测试脚本中硬编码测试数据的话,测试脚本灵活性会非常低.而且,对于那些具有重复的请求,而只是测试入参不 ...

最新文章

  1. ajax 服务器怎么响应,发出请求对象(AJAX)后如何从服务器获取响应
  2. IT专业人士需要知道的2018年七大技术趋势
  3. C++ leetcode 给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。 不要使用额外的数组空间,你必须仅使用 O(1) 额外
  4. 查看php-fpm 占用内存情况
  5. Lanchester战争模型:用可分离变量的微分方程占卜战事
  6. 我的世界服务器显示玩家坐标,我的世界手游版怎么显示玩家坐标
  7. 在计算机系统中 外存储器必须通过,大学计算机基础第4章作业.doc
  8. 第三方网站调用微信公众号的图片被禁止
  9. windows火车刷票小白秘笈
  10. mysql自增id 重置
  11. C10K 和 C1000K
  12. 手把手教你安装JDK免安装版(简单粗暴)
  13. 快排的单指针扫描和双指针扫描
  14. 用MATLAB求一阶微分方程(组)数值解
  15. 使用 nosqlBooster for mongoDB 连接 Mongodb
  16. php程序员需要什么资质,微信小程序服务类目及资质要求
  17. ssh远程出错Add correct host key in /home/xxx/.ssh/known_hosts to get rid of this message
  18. 【模块电路】继电器驱动应用
  19. [574]tf.nn.xw_plus_b
  20. Spring注入Bean的七种方式

热门文章

  1. Sklearn(v3)——SVM理论(1)
  2. 【统计学习】统计学习方法概论
  3. 【风险管理】风控一二三
  4. 跟我学大数据分析:多维度数据分析报告
  5. 利用Hyperledger Fabric开发第一个区块链应用
  6. 关于催收那些事儿(下)
  7. 关于Rocchio算法和向量空间模型反馈
  8. 白话Elasticsearch54-数据建模之通过【应用层join】或者【数据冗余】实现实现用户与博客的关联
  9. als算法参数_Spark2.0协同过滤与ALS算法介绍
  10. 爬虫学习笔记(十五)——加密解密