Mock 是什么
mock 测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。这个虚拟的对象就是mock对象。mock对象就是真实对象在调试期间的代替品。

简单的看一张图

我们在测试类 A 时,类 A 需要调用类 B 和类 C,而类 B 和类 C 又需要调用其他类如 D、E、F 等,假如类 D、E、F 构造很耗时又或者调用很耗时的话是非常不便于测试的(比如是 DAO 类,每次访问数据库都很耗时)。所以我们引入 Mock 对象。

如上图,我们将类 B 和类 C 替换成 Mock 对象,在调用类 B 和类 C 的方法时,用 Mock 对象的方法来替换(当然我们要自己设定参数和期望结果)而不用实际去调用其他类。这样测试效率会高很多。

一句话为什么要 Mock:因为我们实际编写程序都不会是一个简单类,而是有着复杂依赖关系的类,Mock 对象让我们在不依赖具体对象的情况下完成测试。

Mock 的关键点
Mock 对象
模拟对象的概念就是我们想要创建一个可以替代实际对象的对象,这个模拟对象要可以通过特定参数调用特定的方法,并且能返回预期结果。

Stub(桩)
桩指的是用来替换具体功能的程序段。桩程序可以用来模拟已有程序的行为或是对未完成开发程序的一种临时替代。

比如我们有一个获取温度的函数。

public double getTemperature(String Position) {
double ret = TemperatureRead(Position);
}
1
2
3
但是 TemperatureRead 函数要调用具体的硬件设备,而硬件设备没准备好。

我们可以用 Stub 来替换

public double TemperatureRead(String position) {
return 28;
}
1
2
3
Stub:For replacing a method with code that returns a specified result

Mock:A stub with an expectations that the method gets called

设置预期
通过设置预期明确 Mock 对象执行时会发生什么,比如返回特定的值、抛出一个异常、触发一个事件等,又或者调用一定的次数。

验证预期的结果
设置预期和验证预期是同时进行的。设置预期在调用测试类的函数之前完成,验证预期则在它之后。所以,首先你设定好预期结果,然后去验证你的预期结果是否正确。

Mock 的好处是什么
提前创建测试; TDD(测试驱动开发)
这是个最大的好处吧。如果你创建了一个Mock那么你就可以在service接口创建之前写Service Tests了,这样你就能在开发过程中把测试添加到你的自动化测试环境中了。换句话说,模拟使你能够使用测试驱动开发。

团队可以并行工作
这类似于上面的那点;为不存在的代码创建测试。但前面讲的是开发人员编写测试程序,这里说的是测试团队来创建。当还没有任何东西要测的时候测试团队如何来创建测试呢?模拟并针对模拟测试!这意味着当service借口需要测试时,实际上QA团队已经有了一套完整的测试组件;没有出现一个团队等待另一个团队完成的情况。这使得模拟的效益型尤为突出了。

你可以创建一个验证或者演示程序
由于Mocks非常高效,Mocks可以用来创建一个概念证明,作为一个示意图,或者作为一个你正考虑构建项目的演示程序。这为你决定项目接下来是否要进行提供了有力的基础,但最重要的还是提供了实际的设计决策。

为无法访问的资源编写测试
这个好处不属于实际效益的一种,而是作为一个必要时的“救生圈”。有没有遇到这样的情况?当你想要测试一个service接口,但service需要经过防火墙访问,防火墙不能为你打开或者你需要认证才能访问。遇到这样情况时,你可以在你能访问的地方使用MockService替代,这就是一个“救生圈”功能。

Mock 可以交给用户
在有些情况下,某种原因你需要允许一些外部来源访问你的测试系统,像合作伙伴或者客户。这些原因导致别人也可以访问你的敏感信息,而你或许只是想允许访问部分测试环境。在这种情况下,如何向合作伙伴或者客户提供一个测试系统来开发或者做测试呢?最简单的就是提供一个mock,无论是来自于你的网络或者客户的网络。soapUI mock非常容易配置,他可以运行在soapUI或者作为一个war包发布到你的java服务器里面。

隔离系统
有时,你希望在没有系统其他部分的影响下测试系统单独的一部分。由于其他系统部分会给测试数据造成干扰,影响根据数据收集得到的测试结论。使用mock你可以移除掉除了需要测试部分的系统依赖的模拟。当隔离这些mocks后,mocks就变得非常简单可靠,快速可预见。这为你提供了一个移除了随机行为,有重复模式并且可以监控特殊系统的测试环境。

Mockito
介绍
Mockito 是一个简单的流行的 Mock 框架。它能够帮我们创建 Mock 对象,保持单元测试的独立性。

使用它只需要在 Maven 中添加依赖即可。

<!-- https://mvnrepository.com/artifact/org.mockito/mockito-all -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>2.0.2-beta</version>
</dependency>
1
2
3
4
5
6
创建 Mock 对象
通过方法创建
class CreateMock {
@Before
public void setup() {
mockUserDao = mock(UserDao.class);
userService = new UserServiceImpl();
userService.setUserDao(mockUserDao);
}
}
1
2
3
4
5
6
7
8
通过注解创建
class CreateMock {
@Mock
UserDao mockUserDao;

@InjectMocks
private UserServiceImpl userService;

@Before
public void setUp() {
//初始化对象的注解
MockitoAnnotations.initMocks(this);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
一个简单的例子:
package Mockito;

import org.junit.Assert;
import org.junit.Test;
import java.util.List;

import static org.mockito.Mockito.*;

public class MyTest {
@Test
public void myTest() {
/* 创建 Mock 对象 */
List list = mock(List.class);
/* 设置预期,当调用 get(0) 方法时返回 "111" */
when(list.get(0)).thenReturn("111");

Assert.assertEquals("asd", 1, 1);
/* 设置后返回期望的结果 */
System.out.println(list.get(0));
/* 没有设置则返回 null */
System.out.println(list.get(1));

/* 对 Mock 对象设置无效 */
list.add("12");
list.add("123");
/* 返回之前设置的结果 */
System.out.println(list.get(0));
/* 返回 null */
System.out.println(list.get(1));
/* size 大小为 0 */
System.out.println(list.size());

/* 验证操作,验证 get(0) 调用了 2 次 */
verify(list, times(2)).get(0);

/* 验证返回结果 */
String ret = (String)list.get(0);
Assert.assertEquals(ret, "111");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

从上面代码能看出一般使用的步骤:

设置方法预期返回
通过 when(list.get(0)).thenReturn(“111”); 来设置当调用 list.get(0) 时返回 “111”,该方法就是 Stub,替换我们实际的操作。

验证方法调用
该方法验证 get(0) 方法调用的次数 verify(list, times(2)).get(0);,还可以设置是否调用过,调用时间等等。

验证返回值
Assert.assertEquals(ret, “111”); 方法验证 Mock 对象方法调用后的返回值是否达到预期。

Mockito 使用
设置 Mock 对象期望和返回值
/* 表示第一次调用 someMethod() 返回 value1 第二次调用返回 value2 */
when(mock.someMethod()).thenReturn(value1).thenReturn(value2);
when(mock.someMethod()).thenReturn(value1, value2);
/* 也可以设置两次 */
when(mock.someMethod()).thenReturn(value1);
when(mock.someMethod()).thenReturn(value2);
1
2
3
4
5
6
另外一种写法 doReturn()

/* 表示第一次调用 someMethod() 返回 value1 第二次调用返回 value2 */
doReturn(value1).doReturn(value2).when(mock).someMethod();
/* 若返回 void,则设置为 doNothing() */
doNothing().when(mock).someMethod();
1
2
3
4
对方法设定返回异常
/* 当调用 someMethod() 方法时会抛出异常 */
when(mock.someMethod()).thenThrow(new RuntimeException());
/* 对 void 方法设定 */
doThrow(new RuntimeException()).when(mock).someMethod();
1
2
3
4
参数匹配器
我们不一定要固定 Stub 调用时的参数,如 get(0)。可以通过参数匹配器来调用。

when(list.get(anyInt())).thenReturn("hello");
1
Mock 对象的行为验证
package Mockito;

import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.testng.annotations.Test;

import java.util.List;
import static org.mockito.Mockito.*;

/**
* Created by andy.wwh on 2016/7/18.
*/
public class Behavior {
@Test
public void behaviorCheck() {
List mock1 = mock(List.class);
List mock2 = mock(List.class);

/* 设置预期 */
when(mock1.get(0)).thenReturn("hello world");
when(mock1.get(1)).thenReturn("hello world");
when(mock2.get(0)).thenReturn("hello world");
mock1.get(0);

/* 验证方法调用一次 */
verify(mock1).get(0);
mock1.get(0);
/* 验证方法调用两次 */
verify(mock1, times(2)).get(0);
/* 验证方法从未被调用过 */
verify(mock2, never()).get(0);
/* 验证方法 100 毫秒内调用两次 */
verify(mock1, timeout(100).times(2)).get(anyInt());

/* 设置方法调用顺序 */
InOrder inOrder = inOrder(mock1, mock2);
inOrder.verify(mock1, times(2)).get(0);
inOrder.verify(mock2, never()).get(1);

/* 查询是否存在被调用,但未被 verify 验证的方法 */
verifyNoMoreInteractions(mock1, mock2);
/* 验证 Mock 对象是否没有交发生 */
verifyZeroInteractions(mock1, mock2);

/* 参数捕获器 */
ArgumentCaptor<Integer> argumentCaptor = ArgumentCaptor.forClass(Integer.class);
verify(mock1, times(2)).get(argumentCaptor.capture());
System.out.println("argument:" + argumentCaptor.getValue());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
验证调用次数
verify(mock1, timeout(100).times(2)).get(anyInt());

除了代码中的方法,Mockito 还提供了
- never() 没有被调用,相当于times(0)
- atLeast(N) 至少被调用N次
- atLeastOnce() 相当于atLeast(1)
- atMost(N) 最多被调用N次

超时验证
通过 timeout 我们可以进行验证程序执行时间是否符合规则。

方法调用顺序
Inorder 可以验证方法调用的顺序

verifyNoMoreInteractions 和 verifyZeroInteractions
verifyNoMoreInteractions:查询是否存在被调用,但未被 verify 验证的方法

verifyZeroInteractions:verifyZeroInteractions

ArgumentCaptor 参数捕获器
可在验证时对方法的参数进行捕获,最后验证捕获的参数值。如果方法有多个参数都要捕获验证,那就需要创建多个ArgumentCaptor对象处理。

Spy 对象验证
Mock 操作的全是虚拟对象。即使我们设置了 when(list.get(0)).thenReturn(1),我们调用如 size() 方法返回的还是 0。Mockito 还给我们提供了一种对真实对象操作的方法——Spy

做一个简单的比较:

package Mockito;

import org.testng.annotations.Test;

import java.util.List;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

/**
* Created by andy.wwh on 2016/7/18.
*/
public class MockObject {
@Test
public void MockTest() {
List list = mock(List.class);

when(list.get(0)).thenReturn("hello world");

System.out.println(list.get(0));

System.out.println(list.size());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

package Mockito;

import org.testng.annotations.Test;

import java.util.LinkedList;
import java.util.List;

import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

/**
* Created by andy.wwh on 2016/7/18.
*/
public class MockObject {
@Test
public void MockTest() {
/* 创建真实对象 */
List list = new LinkedList();
List spy = spy(list);

spy.add("hello");

when(spy.get(0)).thenReturn("hello world");

System.out.println(spy.get(0));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

看个官网的例子:

@Test
public void spyTest() {
List list = new LinkedList();
List spy = spy(list);
// optionally, you can stub out some methods:
when(spy.size()).thenReturn(100);
// using the spy calls real methods
spy.add("one");
spy.add("two");
// prints "one" - the first element of a list
System.out.println(spy.get(0));
// size() method was stubbed - 100 is printed
System.out.println(spy.size());
// optionally, you can verify
verify(spy).add("one");
verify(spy).add("two");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
参考:

Mockito 初探

Mockito:一个强大的用于 Java 开发的模拟测试框架
---------------------
作者:I_myours
来源:CSDN
原文:https://blog.csdn.net/wwh578867817/article/details/51934404
版权声明:本文为博主原创文章,转载请附上博文链接!

Mock 模拟测试简介及 Mockito 使用入门的更多相关文章

  1. springboot2.0入门(四)----mock模拟测试+单元测试

    一.本节主要记录模拟测试.单元测试: 二.mock 测试 1.1什么是Mock? 在面向对象程序设计中,模拟对象(英语:mock object,也译作模仿对象)是以可控的方式模拟真实对象行为的假的对象 ...

  2. Python 的mock模拟测试介绍

    如何不靠耐心测试 可能我们正在写一个社交软件并且想测试一下"发布到Facebook的功能",但是我们不希望每次运行测试集的时候都发布到Facebook上. Python的unitt ...

  3. 使用PowerMockito和Mockito进行模拟测试,包括静态方法测试,私有方法测试等,以及方法执行的坑或者模拟不成功解决

    依赖:这个很重要,不同版本用法也有点区别: <dependency> <groupId>org.mockito</groupId> <artifactId&g ...

  4. Mockito:一个强大的用于Java开发的模拟测试框架

    https://blog.csdn.net/zhoudaxia/article/details/33056093 介绍 本文将介绍模拟测试框架Mockito的一些基础概念, 介绍该框架的优点,讲解应用 ...

  5. Spring Boot中采用Mockito来mock所测试的类的依赖(避免加载spring bean,避免启动服务器)

    最近试用了一下Mockito,感觉真的挺方便的.举几个应用实例: 1,需要测试的service中注入的有一个dao,而我并不需要去测试这个dao的逻辑,只需要对service进行测试.这个时候怎么办呢 ...

  6. 利用Python中的mock库对Python代码进行模拟测试

    这篇文章主要介绍了利用Python中的mock库对Python代码进行模拟测试,mock库自从Python3.3依赖成为了Python的内置库,本文也等于介绍了该库的用法,需要的朋友可以参考下     ...

  7. 【转】利用Python中的mock库对Python代码进行模拟测试

    出处 https://www.toptal.com/python/an-introduction-to-mocking-in-python http://www.oschina.net/transla ...

  8. Mac下Jmeter快速安装与入门-模拟测试Post请求及设置Http头

    [1]去Apache官网下载 Binaries系列的最新Jmeter.gz包 [2]下载到本地之后解压缩,进入到解压之后的目录然后,找到apache-jmeter-4.0/bin/jmeter.sh ...

  9. Android单元测试与模拟测试详解

    测试与基本规范 为什么需要测试? 为了稳定性,能够明确的了解是否正确的完成开发. 更加易于维护,能够在修改代码后保证功能不被破坏. 集成一些工具,规范开发规范,使得代码更加稳定( 如通过 phabri ...

随机推荐

  1. 005.基于docker部署etcd集群部署

    一 环境准备 ntp配置:略 #建议配置ntp服务,保证时间一致性 etcd版本:v3.3.9 防火墙及SELinux:关闭防火墙和SELinux 名称 地址 主机名 备注 etcd1 172.24. ...

  2. python基础下的数据结构与算法之顺序表

    一.什么是顺序表: 线性表的两种基本的实现模型: 1.将表中元素顺序地存放在一大块连续的存储区里,这样实现的表称为顺序表(或连续表).在这种实现中,元素间的顺序关系由它们的存储顺序自然表示. 2.将表 ...

  3. Spring框架学习——Spring的体系结构详解

    1.Spring简介 Spring是一个轻量级Java开发框架,最早有Rod Johnson创建,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题.它是一个分层的JavaSE/JavaEE ...

  4. ArduinoYun教程之OpenWrt-Yun与CLI配置Arduino Yun

    ArduinoYun教程之OpenWrt-Yun与CLI配置Arduino Yun OpenWrt-Yun OpenWrt-Yun是基于OpenWrt的一个Linux发行版.有所耳闻的读者应该听说他是 ...

  5. C/C++ 和 PHP 技术经典图书,学习视频资料总结

    技术经典图书 1.<计算机科学导论> 作者:(美)佛罗赞,(美)莫沙拉夫著,刘艺等译(强推) 涵盖了大部分计算机课程的内容,但都是简介,是最基础的知识,非常适合计算机初学者看,强烈建议把课 ...

  6. Intel Code Challenge Elimination Round (Div.1 + Div.2, combined) C. Destroying Array 带权并查集

    C. Destroying Array 题目连接: http://codeforces.com/contest/722/problem/C Description You are given an a ...

  7. 微信支付回调取不到body体中的信息node.js

    因为支付回调返回的数据格式为XML数据格式,需要安装组件body-parser-xml 安装语法:   npm install body-parser-xml --save 在app.js 文件中引入 ...

  8. Java_Certificates does not conform to algorithm constraints

    java.security.cert.CertificateException: Certificates does not conform to algorithm constraints SSL证 ...

  9. 一、java概述

    一.概述 java不仅仅是一门编程语言,还是一个由一系列计算机软件和规范形成的技术体系. 提供了完整的开发和跨平台部署的支持环境.用途广泛.     结构严谨.面向对象.摆脱硬件平台的束缚.     ...

  10. spring cloud 学习(9) - turbine stream无法在eureka注册的解决办法

    turbine是啥就不多解释了,初次接触的可以移步spring cloud 学习(4) - hystrix 服务熔断处理 拉到最后看一下,turbine stream默认情况下启动成功后,eureka ...