一、什么是 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. SGDClassifier梯度下降分类方法

    SGDClassifier梯度下降分类方法 这个分类器跟其他线性分类器差不多,只是它用的是mini-batch来做梯度下降,在处理大数据的情况下收敛更快 1.应用 SGD主要应用在大规模稀疏数据问题上 ...

  2. [PAT] A1019 General Palindromic Number

    [题目] https://pintia.cn/problem-sets/994805342720868352/problems/994805487143337984 题目大意:给定一个N和b,求N在b ...

  3. os常用讲解

    os.mkdir()创建单个不存在的空目录,无法创建多个或者已经存在的含有文件的同名目录 os.makedirs() 能够递归创建多个目录,如果目录已经存在即使都是空的或者目录已经存在且含有文件,则引 ...

  4. LAMP(六)之以CentOS6自带的rpm包组合安装lamp

    1.Centos7部署应用wordpress 1. 安装php.php-mysql.mariadb yum install php php-mysql mariadb-server 2. 测试 cd ...

  5. 移动端 h5

    取消点击时候的透明区域 -webkit-tap-highlight-color: rgba(0,0,0,0); iphonex 适配 <meta name="viewport" ...

  6. 18新生赛 4. Deal

    题目描述:双十一过后,syx发现自己快要吃土了.但是机智的他决定理财.他预测了将来n天的比特币行情,发现有涨有跌,有跌有涨.手里的钱只要在比特币的浪潮中经历沉浮,低价收入,高价卖出,就可以轻易割到别人 ...

  7. Linux下用Bash语言实现输出最大值的功能

    题目链接: 题目描述 编写一个程序,输入a.b.c三个值,输出其中最大值. 输入 一行数组,分别为a b c 输出 a b c其中最大的数 样例输入 10 20 30 样例输出 30 复习下Linux ...

  8. 云服务器 使用 onedrive 快速同步

    重大更新:支持微软的onedrive网盘,可以自动实时双向同步数据,也可以多台服务器和网盘之间实时同步数据.新增了一个虚拟环境python367,支持pytorch1.2:-----------微软O ...

  9. 获取mybaties插入记录自动增长的主键值

    首先在Mybatis Mapper文件中insert语句中添加属性“useGeneratedKeys”和“keyProperty”,其中keyProperty是保存主键值的属性. 例如: <in ...

  10. 2.4测试赛AC代码临时保存

    //H #include<cstdio> #include<cstdlib> #include<cstring> #include<stack> usi ...