一、什么是 Mock 测试

Mock 测试就是在测试过程中,对于某些不容易构造(如 HttpServletRequest 必须在Servlet 容器中才能构造出来)或者不容易获取比较复杂的对象(如 JDBC 中的ResultSet 对象),用一个虚拟的对象(Mock 对象)来创建以便测试的测试方法。Mock 最大的功能是帮你把单元测试的耦合分解开,如果你的代码对另一个类或者接口有依赖,它能够帮你模拟这些依赖,并帮你验证所调用的依赖的行为。
先来看看下面这个示例:

从上图可以看出如果我们要对A进行测试,那么就要先把整个依赖树构建出来,也就是BCDE的实例。
一种替代方案就是使用mocks

从图中可以清晰的看出
mock对象就是在调试期间用来作为真实对象的替代品。
mock测试就是在测试过程中,对那些不容易构建的对象用一个虚拟对象来代替测试的方法就叫mock测试。

二、Mockito是什么

Mockito 是一个流行 mock 框架,可以和JUnit结合起来使用。Mockito 允许你创建和配置 mock 对象。使用Mockito可以明显的简化对外部依赖的测试类的开发。

I notice that Mockito was voted "the best mock framework for Java" on Stackoverflow.

三、使用方法

1、添加Maven依赖

<!-- mockito -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.8.5</version>
<scope>test</scope>
</dependency>

如果使用springboot的话,有自带可以不用引入

2、建议静态导入会使代码更简洁

import static org.mockito.Mockito.*;
import static org.junit.Assert.*;

3、例子

3.1 创建一个mock对象,并校验

@Test
public void Demo1() {
// mock creation 创建mock对象
List mockedList = mock(List.class); //using mock object 使用mock对象
mockedList.add("one");
mockedList.clear(); //verification 验证
verify(mockedList).add("one");
verify(mockedList).clear();
}

3.2 使用测试桩
默认情况下,所有的函数都有返回值。mock函数默认返回的是null,一个空的集合或者一个被对象类型包装的内置类型,例如0、false对应的对象类型为Integer、Boolean;
测试桩函数可以被覆写 : 例如常见的测试桩函数可以用于初始化夹具,但是测试函数能够覆写它。请注意,覆写测试桩函数是一种可能存在潜在问题的做法;
一旦测试桩函数被调用,该函数将会一致返回固定的值;
上一次调用测试桩函数有时候极为重要-当你调用一个函数很多次时,最后一次调用可能是你所感兴趣的。

@Test
public void Demo2() {
// 你可以mock具体的类型,不仅只是接口
LinkedList mockedList = mock(LinkedList.class); // 测试桩(可以使用连续调用)
when(mockedList.get(0)).thenReturn("first");
//when(mockedList.get(0)).thenReturn("first").thenReturn("second");
when(mockedList.get(1)).thenThrow(new RuntimeException()); // 输出“first”
System.out.println(mockedList.get(0));
// 连续调用则输出“second”
//System.out.println(mockedList.get(0)); // 抛出异常
System.out.println(mockedList.get(1)); // 因为get(999) 没有打桩,因此输出null
System.out.println(mockedList.get(999)); // 验证get(0)被调用的次数
verify(mockedList).get(0);
}

3.3 验证函数的确切、最少、从未调用次数

@Test
public void Demo3() {
// 你可以mock具体的类型,不仅只是接口
LinkedList mockedList = mock(LinkedList.class);
//using mock
mockedList.add("once"); mockedList.add("twice");
mockedList.add("twice"); mockedList.add("three times");
mockedList.add("three times");
mockedList.add("three times"); // 下面的两个验证函数效果一样,因为verify默认验证的就是times(1)
verify(mockedList).add("once");
verify(mockedList, times(1)).add("once"); // 验证具体的执行次数
verify(mockedList, times(2)).add("twice");
verify(mockedList, times(3)).add("three times"); // 使用never()进行验证,never相当于times(0)
verify(mockedList, never()).add("never happened"); // 使用atLeast()/atMost()
verify(mockedList, atLeastOnce()).add("three times"); // mockedList.add("five times");
// mockedList.add("five times");
// mockedList.add("five times");
// mockedList.add("three times");
// mockedList.add("three times");
// mockedList.add("three times");
//最少or最多
verify(mockedList, atLeast(2)).add("five times");
verify(mockedList, atMost(5)).add("three times"); }

3.4 模拟异常

@Test
public void Demo4() { LinkedList mockedList = mock(LinkedList.class);
doThrow(new RuntimeException()).when(mockedList).clear(); // 调用这句代码会抛出异常
mockedList.clear();
}

3.5 验证执行执行顺序

@Test
public void Demo5() {
// A. 验证mock一个对象的函数执行顺序
List singleMock = mock(List.class); singleMock.add("was added first");
singleMock.add("was added second");
singleMock.contains("111");
singleMock.isEmpty();
singleMock.remove(0);
singleMock.get(0); // 为该mock对象创建一个inOrder对象
InOrder inOrder = inOrder(singleMock); // 确保add函数首先执行的是add("was added first"),然后才是add("was added second")
inOrder.verify(singleMock).add("was added first");
inOrder.verify(singleMock).add("was added second");
inOrder.verify(singleMock).contains("111");
inOrder.verify(singleMock).isEmpty();
inOrder.verify(singleMock).remove(0);
inOrder.verify(singleMock).get(0); // // B .验证多个mock对象的函数执行顺序
// List firstMock = mock(List.class);
// List secondMock = mock(List.class);
//
// firstMock.add("was called first");
// secondMock.add("was called second");
//
// // 为这两个Mock对象创建inOrder对象
// InOrder inOrder = inOrder(firstMock, secondMock);
//
// // 验证它们的执行顺序
// inOrder.verify(firstMock).add("was called first");
// inOrder.verify(secondMock).add("was called second"); // Oh, and A + B can be mixed together at will
}

3.6 spy
然而很多时候,你希望达到这样的效果:除非指定,否者调用这个对象的默认实现,同时又能拥有验证方法调用的功能。这正好是spy对象所能实现的效果。创建一个spy对象,以及spy对象的用法介绍如下:

@Test
public void Demo6() { List list = new LinkedList();
List spy = spy(list); // 你可以为某些函数打桩
when(spy.size()).thenReturn(100); // 通过spy对象调用真实对象的函数
spy.add("one");
spy.add("two"); // 输出第一个元素
System.out.println(spy.get(0)); // 因为size()函数被打桩了,因此这里返回的是100
System.out.println(spy.size()); // 交互验证
verify(spy).add("one");
verify(spy).add("two");
}

3.7 在监控对象上使用when(Object)来进行打桩是不可能或者不切实际的。
因此,当使用监控对象时请考虑doReturn|Answer|Throw()函数族来进行打桩。

@Test
public void Demo7(){
List list = new LinkedList();
List spy = spy(list); // 不可能 : 因为当调用spy.get(0)时会调用真实对象的get(0)函数,此时会发生IndexOutOfBoundsException异常,因为真实List对象是空的
//when(spy.get(0)).thenReturn("foo"); // 你需要使用doReturn()来打桩
doReturn("foo").when(spy).get(anyInt()); System.out.println(spy.get(0));
// doThrow(new RuntimeException()).when(spy).get(anyInt());
// System.out.println(spy.get(0));
}

3.8使用注解形式来模拟测试对象,必须在初始化fields (领域),有2种方式初始化:

@RunWith(@MockitoJUnitRunner.class) 标注 JUnit 测试类
@Before 之前调用 MockitoAnnotations.initMocks(Object)
@Before
public void init(){
MockitoAnnotations.initMocks(this);
}

或者

@RunWith(MockitoJUnitRunner.class)

这里补充一下几个注解的作用

一般项目测试是用@RunWith(SpringRunner.class)

这个运行器来启动的,有些教程使用的是SpringJUnit4ClassRunner这个运行器,我们可以查看源码,其实是同一个东西,

public final class SpringRunner extends SpringJUnit4ClassRunner {
public SpringRunner(Class<?> clazz) throws InitializationError {
super(clazz);
}
}

3.9 参数捕获

ArgumentCaptor类允许我们在verification期间访问方法的参数。得到方法的参数后我们可以使用它进行测试。
ArgumentCaptor是一个能够捕获参数值的特殊参数匹配器。捕获一个
Mock 对象的方法调用所传递的参数

@Test
public void testCaptureArgument() {
List<String> list = Arrays.asList("1", "2");
List mockedList = mock(List.class);
ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
mockedList.addAll(list);
verify(mockedList).addAll(argument.capture()); assertEquals(2, argument.getValue().size());
assertEquals(list, argument.getValue());
}

3.10 RETURNS_SMART_NULLS 和 RETURNS_DEEP_STUBS

RETURNS_SMART_NULLS

(返回默认值-查看源码: ReturnsMoreEmptyValues)
RETURNS_SMART_NULLS
在创建mock对象时,有的方法我们没有进行stubbing,所以在调用的时候有时会返回Null这样在进行处理时就很可能抛出NullPointerException。如果通过RETURNS_SMART_NULLS参数来创建的mock对象在调用没有stubbed的方法时他将返回SmartNull。例如:返回类型是String它将返回空字符串””;是int,它将返回0;如果是List,它会返回一个空的List。另外,在堆栈中可以看到SmartNull的友好提示。

由于使用了RETURNS_SMART_NULLS参数来创建mock对象,所以在执行下面的操作时将不会抛出NullPointerException异常,另外堆栈也提示了相关的信息“SmartNull returned by unstubbed get() method on mock”。

@Test
public void returnsSmartNullsTest() {
//List mock = mock(List.class);
List mock = mock(List.class, RETURNS_SMART_NULLS);
System.out.println(mock.get(0)); // 使用RETURNS_SMART_NULLS参数创建的mock对象,不会抛出NullPointerException异常。
// 另外控制台窗口会提示信息“SmartNull returned by unstubbed get() method on mock”
System.out.println(mock.toArray().length);
}

RETURNS_DEEP_STUBS

(判断是否需要返回默认值,否则创建mock对象 ReturnsDeepStubs)
RETURNS_DEEP_STUBS参数程序会自动进行mock所需的对象,方法deepstubsTest和deepstubsTest2是等价的

public class FakeEntity {
private UserFakeEntity userFakeEntity; public UserFakeEntity getUserFakeEntity() {
return userFakeEntity;
} public void setUserFakeEntity(UserFakeEntity userFakeEntity) {
this.userFakeEntity = userFakeEntity;
}
} @Test
public void deepstubsTest() {
//FakeEntity fakeEntity = mock(FakeEntity.class);//这样会NullPointerException
FakeEntity fakeEntity = mock(FakeEntity.class, RETURNS_DEEP_STUBS);
when(fakeEntity.getUserFakeEntity().getName()).thenReturn("Beijing");
System.out.println(fakeEntity.getUserFakeEntity().getName());
assertEquals("Beijing", fakeEntity.getUserFakeEntity().getName());
} @Test
public void deepstubsTest2() {
FakeEntity fakeEntity = mock(FakeEntity.class);
UserFakeEntity userFakeEntity = mock(UserFakeEntity.class); when(fakeEntity.getUserFakeEntity()).thenReturn(userFakeEntity);
when(userFakeEntity.getName()).thenReturn("Beijing");
System.out.println(fakeEntity.getUserFakeEntity().getName());
assertEquals("Beijing", fakeEntity.getUserFakeEntity().getName());
}

四、实战使用

4.1登录的例子

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.boot.test.context.SpringBootTest; import static org.mockito.Mockito.*; //基于MockitoJUnitRunner的运行器
@RunWith(MockitoJUnitRunner.class)
//@RunWith(SpringRunner.class)
@SpringBootTest
public class MockLoginTest { //自动将模拟对象或侦查域注入到被测试对象中。
//对被测类中@Autowired的对象,用@Mocks标注;对被测类自己,用@InjectMocks标注
@InjectMocks
private UserServiceImpl userService; @Mock
private UserRepository userDao; @Mock
private BargainBlackListService bargainBlackListService; @Mock
private DistributionUserService distributionUserService; @Mock
private AuthorityService authorityService; @Before
public void init(){
MockitoAnnotations.initMocks(this);
} @Test
public void loginTest() {
//模拟方法动作
//spyUserService在调用getUserEntity方法的时候,指定返回事先定义好的userEntity
UserService spyUserService = spy(userService);
UserEntity userEntity = new UserEntity();
userEntity.setUserName("ppp");
doReturn(userEntity).when(spyUserService).getUserEntity(anyString());
when(bargainBlackListService.checkBlackList(anyString())).thenReturn(true);
UserInfoDTO userInfoDTO = new UserInfoDTO();
userInfoDTO.setUserName("ppp");
userInfoDTO.setId("2c95808a644ade6801644ae37f730000");
spyUserService.updateUserInfo("0520b1d9-9806-4ec1-a095-dfcd8bea8fd6", userInfoDTO);
} }

mockito使用教程的更多相关文章

  1. mockito简单教程

    注:本文来源:sdyy321的<mockito简单教程> 官网: http://mockito.org API文档:http://docs.mockito.googlecode.com/h ...

  2. 使用 Mockito 单元测试 – 教程

    tanyuanji@126.com 版本历史 - - - - 使用 Mockito 进行测试 该教程主要讲解 Mockito 框架在Eclipse IDE 中的使用   目录 tanyuanji@12 ...

  3. Mockito 简明教程

    什么是 Mock 测试 Mock 测试就是在测试过程中,对于某些不容易构造(如 HttpServletRequest 必须在Servlet 容器中才能构造出来)或者不容易获取比较复杂的对象(如 JDB ...

  4. Mockito教程

    Mockito教程 2017-01-20 目录 1 Mockito 介绍   1.1 Mockito是什么?  1.2 为什么需要Mock  1.3 Stub和Mock异同  1.4 Mockito资 ...

  5. Mockito 简介

    Mockito 是一种 Java Mock 框架,主要是用来做 Mock 测试,它可以模拟任何 Spring 管理的 Bean.模拟方法的返回值.模拟抛出异常等等,在了解 Mockito 的具体用法之 ...

  6. 学习 Spring Boot:(二十九)Spring Boot Junit 单元测试

    前言 JUnit 是一个回归测试框架,被开发者用于实施对应用程序的单元测试,加快程序编制速度,同时提高编码的质量. JUnit 测试框架具有以下重要特性: 测试工具 测试套件 测试运行器 测试分类 了 ...

  7. 【项目经验】Mockito教程

    一.教程 转载:https://blog.csdn.net/sdyy321/article/details/38757135/ 官网: http://mockito.org API文档:http:// ...

  8. Mockito框架入门教程(一)

    官网: http://mockito.org API文档:http://docs.mockito.googlecode.com/hg/org/mockito/Mockito.html 项目源码:htt ...

  9. Mockito框架入门教程(二)

    接上一篇,继续学习其它的.... 8.找出冗余的互动(即未被验证到的) @Test(expected = NoInteractionsWanted.class) public void find_re ...

随机推荐

  1. 预防XSs和sql注入常见分析

    SQL注入简介SQL 注入漏洞(SQL Injection)是 Web 开发中最常见的一种安全漏洞.可以用它来从数据库获取敏感信息,或者利用数据库的特性执行添加用户,导出文件等一系列恶意操作,甚至有可 ...

  2. codechef Chef and The Colored Grid

    难度 \(hard\) 题意 \(3\times n\)的方格,前两行已分别填入\(n-\)排列,要求求第三行填入\(n-\)排列,使得每行每列数不重复的方案数(数据保证前两行合法)\(n\le 10 ...

  3. 5G PDCCH 协议

    For downlink, a maximum of 16 HARQ processes per cell is supported by the UE. The number of processe ...

  4. PAT (Basic Level) Practice (中文)1038 统计同成绩学生 (20 分)

    本题要求读入 N 名学生的成绩,将获得某一给定分数的学生人数输出. 输入格式: 输入在第 1 行给出不超过 1 的正整数 N,即学生总人数.随后一行给出 N 名学生的百分制整数成绩,中间以空格分隔.最 ...

  5. WeUI基础样式库——写一个移动端界面

    WeUI是一套基础样式库,同微信原生视觉体验一致,由微信官方设计团队为微信内网页和微信小程序量身设计的.我们来看看这个基础库样式到底长什么样. 这些密密麻麻的就是压缩后的样式库.密密麻麻地看起来简直要 ...

  6. C++ const char *返回值问题

    今天写代码,遇到一个const char *返回值的问题,记录一下 问题场景:我写了一个动态库,有个函数声明如下: ; 函数定义如下: const char * HttpRequestImpl::RG ...

  7. weblogic的ssrf漏洞

    前言    什么是ssrf SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成由服务端发起请求的一个安全漏洞. 一般情况下,SSRF攻击的目标 ...

  8. 测试用例与PUCCH

  9. 《深入理解java虚拟机》读书笔记十一——第十二章

    第十二章  Java内存模型与线程 1.硬件效率与一致性 由于计算机的存储设备与处理器的运算速度有几个数量级的差距,所以现代计算机系统都不得不加入一层读写速度尽可能接近处理器运算速度的高速缓存(Cac ...

  10. malloc函数动态分配内存

    #include <stdio.h> #include <stdlib.h> //malloc free #include <windows.h> //sleep ...