Java Mockito 笔记
Mockito
1 Overview
2 Maven 项目初始化
3 示例
3.1 第一个示例
3.2 自动 Mock
3.3 Mock 返回值
3.4 Mock 参数
3.5 自动注入 Mock 对象
3.6 验证调用次数
3.7 预设 Exception
3.8 Void Mock
3.9 级联 Mock
3.10 部分 Mock
4 FAQ
4.1 注意点
5 References
1 Overview
Mockito 是 Java 中用于 Mock 的一个开源项目。
Mock 用于如下目的
- 如果依赖的外部系统在测试环境下不可用,使用 Mock 跳过外部系统调用并模拟返回结果
- 验证是否的确调用了 Mock 对象
2 Maven 项目初始化
创建 Maven Quick Start 项目
pom.xml 修改如下
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.lld</groupId>
<artifactId>test.mockito</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>test.mockito</name>
<url>http://maven.apache.org</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
</parent> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3 示例
3.1 第一个示例
我们先看如下代码
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import java.util.List; import org.junit.Test; public class FirstTest { @Test
public void verifyBehavior() {
@SuppressWarnings("unchecked")
List<Integer> mock = mock(List.class);
mock.add(1);
mock.clear();
verify(mock).add(1);
verify(mock).clear();
}
}
作为第一个示例,我在此详细解释一下
首先,我们使用 Mockito.mock() 方来来生成 Mock 对象。这里我们需要注意两点:
- 可以直接生成接口的 Mock 对象
- 范型对象 Mock
然后我们调用了 Mock 对象的两个成员方法,在此我们需要更深刻地理解一下 Mock 对象的行为,如果我们在调用 add 方法后打印它的 size,我们会发现结果是 0 而不是 1。也就是说,Mock 对象只是拦截了所有对原始方法的调用并返回对应返回的类型的默认值,而不是真正地实现了这个接口或创建了对象实例。
然后是两个 verify 方法,表示验证 Mock 对象是否调用了对应的方法。注意验证 add 调用时,可以验证输入参数(本例为 1),如果不想验证,只需要确定是否调用,可以使用如下方式验证
import static org.mockito.Matchers.anyInt;
...
verify(mock).add(anyInt());
3.2 自动 Mock
可以使用如下方式自动生成 Mock 对象
import org.junit.Before;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
...
@Mock
private List<Integer> mock; @Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
3.3 Mock 返回值
下例 mock一个Iterator类,预设当iterator调用next()时第一次返回hello,以后每次都返回world
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import java.util.Iterator;
import org.junit.Test; public class MockReturnTest { @Test
public void verifyBehavior1() {
@SuppressWarnings("unchecked")
Iterator<String> iterator = mock(Iterator.class);
when(iterator.next()).thenReturn("hello").thenReturn("world");
String result = iterator.next() + " " + iterator.next() + " " + iterator.next();
assertEquals("hello world world", result);
}
}
3.4 Mock 参数
可以使用以下 Mockito 对象来模拟任意输入值
import static org.mockito.Matchers.any*
例如 anyString, anyInteger, anyChar 等,也可以使用 any() 方法来生成任意对象,例如
List<String> mock = mock(List.class);
mock.add(any(String.class));
或者使用更简单的 Mockito.any(), 如下所示
import static org.mockito.Mockito.any; List<String> mock = mock(List.class);
mock.add(any());
3.5 自动注入 Mock 对象
对于如下的情况,我们需要 Mock 某对象的内部方法,如下所示,我们需要 Mock MainServer 内部的 OtherService:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; @Component
public class MainService {
@Autowired
OtherService otherService; public void call() {
System.out.println("value is: " + otherService.getValue());
}
}
import org.springframework.stereotype.Component; @Component
public class OtherService {
public String getValue() {
return "real value";
}
}
常规情况下,我们需要手工注入 Mock 对象,如下所示:
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = DemoConfig.class)
public class AutoInjectTest {
@Autowired
MainService service; @Mock
OtherService otherService; @Test
public void manualInjectTest() {
otherService = mock(OtherService.class);
when(otherService.getValue()).thenReturn("mock value");
service.setOtherService(otherService);
service.call();
}
}
PS:需要在 MainService 类中添加 setOtherService() 方法以允许修改 otherService
其中 DemoConfig 是配置类,对于 Spring Boot 框架,Test 类不会自动注入 Autowired 对象,需要使用 Config 类指定加载类,内容如下:
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; @Configuration
@ComponentScan({ "com.lld.test" })
public class DemoConfig { }
但更合理的方式是使用 @InjectMocks 注解来自动注入,如下所示
import static org.mockito.Mockito.when; 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.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = DemoConfig.class)
public class AutoInjectTest {
@Autowired
@InjectMocks
MainService service; @Mock
OtherService otherService; @Before
public void setup() {
MockitoAnnotations.initMocks(this);
when(otherService.getValue()).thenReturn("mock value");
} @Test
public void autoInjectTest() {
service.call();
}
}
注意如下几点:
- 在 @Before 方法中初始化 Mock 对象及自动注入
- 在需要自动注入成员的类上添加 @InjectMocks 注解
另外值得注意的是,@InjectMocks 只会注入当前对象的成员,不会递归深度注入对象,例如,我们如果将 MainService 修改如下:
@Component
public class MainService {
@Autowired
MiddleService middleService; public void callMiddle() {
System.out.println("value is: " + middleService.getValue());
}
}
添加 MiddleService 如下所示
@Component
public class MiddleService {
@Autowired
OtherService otherService; public String getValue() {
return otherService.getValue();
}
}
这样的话,Unit Test 需要修改如下:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = DemoConfig.class)
public class AutoInjectTest {
@Autowired
MainService service; @Mock
OtherService otherService; @Autowired
@InjectMocks
MiddleService middleService; @Before
public void setup() {
MockitoAnnotations.initMocks(this);
when(otherService.getValue()).thenReturn("mock value");
} @Test
public void autoInjectDeepTest() {
service.callMiddle();
}
}
3.6 验证调用次数
如下代码验证了 add 方法需要被调用两次
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.List;
import org.junit.Test; public class CallTimesTest {
@Test
public void verifyBehavior1() {
List<Integer> mock = mock(List.class);
mock.add(1);
mock.add(1);
verify(mock, times(2)).add(1);
}
}
3.7 预设 Exception
下面代码演示了预设 Exceptio 发生
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import java.io.IOException;
import java.io.OutputStream;
import org.junit.Test; public class ExceptionTest {
@Test(expected = IOException.class)
public void when_thenThrow() throws IOException {
OutputStream outputStream = mock(OutputStream.class);
doThrow(new IOException()).when(outputStream).close();
outputStream.close();
}
}
代码说明如下:
- @Test(expected = IOException.class) 表示该测试需要有 IOException 抛出
- doThrow 表示指定操作将抛出指定异常
3.8 Void Mock
前面的 thenReturn 只适用于有返回值的方法,本例讲述如何 Mock void 方法
声明服务类如下
package com.lld.test.mockito; import org.springframework.stereotype.Component; @Component
public class VoidService {
public void sayHi(String name) {
System.out.println("Hello, " + name);
}
}
测试类如下所示
package com.lld.test.mockito; import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = DemoConfig.class)
public class VoidTest {
@Mock
VoidService voidService; @Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
} @Test
public void voidTest() {
doNothing().when(voidService).sayHi(anyString());
voidService.sayHi("Lindong");
verify(voidService, times(1)).sayHi(anyString());
} @Test
public void voidArgumentTest() {
ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class);
doNothing().when(voidService).sayHi(argumentCaptor.capture());
voidService.sayHi("Lindong");
assertEquals("Lindong", argumentCaptor.getValue());
} @Test
public void answerTest() {
doAnswer(answer -> {
String name = answer.getArgumentAt(0, String.class);
System.out.println("invoke VoidService with argument: " + name);
return null;
}).when(voidService).sayHi(anyString());
voidService.sayHi("Lindong");
}
}
代码说明如下
- voidTest 演示了如何简单地 Mock void 方法
- voidArgumentTest 演示了如何获取 void 方法的参数
- answerTest 演示了如何截获 void 调用
3.9 级联 Mock
本例演示了如何自动 Mock 所有对象下的子对象
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import org.junit.Test; public class DeepMockTest {
@Test
public void deepstubsAutoTest() {
Account account = mock(Account.class, RETURNS_DEEP_STUBS);
when(account.getRailwayTicket().getDestination()).thenReturn("Beijing");
account.getRailwayTicket().getDestination();
verify(account.getRailwayTicket()).getDestination();
assertEquals("Beijing", account.getRailwayTicket().getDestination());
} @Test
public void deepstubsManualTest() {
Account account = mock(Account.class);
RailwayTicket railwayTicket = mock(RailwayTicket.class);
when(account.getRailwayTicket()).thenReturn(railwayTicket);
when(railwayTicket.getDestination()).thenReturn("Beijing"); account.getRailwayTicket().getDestination();
verify(account.getRailwayTicket()).getDestination();
assertEquals("Beijing", account.getRailwayTicket().getDestination());
} public class RailwayTicket {
private String destination; public String getDestination() {
return destination;
} public void setDestination(String destination) {
this.destination = destination;
}
} public class Account {
private RailwayTicket railwayTicket; public RailwayTicket getRailwayTicket() {
return railwayTicket;
} public void setRailwayTicket(RailwayTicket railwayTicket) {
this.railwayTicket = railwayTicket;
}
}
}
代码说明如下:
- deepstubsAutoTest 演示了自动创建子对象的 Mock (推荐)
- deepstubsManualTest 演示了手动创建子对象的 Mock
3.10 部分 Mock
如下例所示,我们需要使用 Mock 跳过 Exception
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.spy; import org.junit.Test; public class PartialMockTest { @Test
public void partialMockTest() throws Exception {
TestObj mockObj = spy(new TestObj());
doNothing().when(mockObj).m1();
mockObj.m3();
} class TestObj {
public void m1() throws Exception {
throw new Exception("exception");
} public void m2() {
System.out.println("m2 is invoked");
} public void m3() throws Exception {
m1();
m2();
}
}
}
我们使用了 spy 方法,它返回的对象是一个真实的对象,所有的方法调用也都是真的方法调用。但像例子中演示的,可以 Mock 掉指定的方法。如果有返回值,也可以和以前的例子一样使用 thenReturn。
4 FAQ
4.1 注意点
对于 @Mock 标注,MockitoAnnotations.initMocks(this); 一定要放在第一行
Mock 对象的所有方法均为假方法,而不是默认实现
5 References
Java Mockito 笔记的更多相关文章
- 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁
什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...
- 0035 Java学习笔记-注解
什么是注解 注解可以看作类的第6大要素(成员变量.构造器.方法.代码块.内部类) 注解有点像修饰符,可以修饰一些程序要素:类.接口.变量.方法.局部变量等等 注解要和对应的配套工具(APT:Annot ...
- Java学习笔记(04)
Java学习笔记(04) 如有不对或不足的地方,请给出建议,谢谢! 一.对象 面向对象的核心:找合适的对象做合适的事情 面向对象的编程思想:尽可能的用计算机语言来描述现实生活中的事物 面向对象:侧重于 ...
- 0032 Java学习笔记-类加载机制-初步
JVM虚拟机 Java虚拟机有自己完善的硬件架构(处理器.堆栈.寄存器等)和指令系统 Java虚拟机是一种能运行Java bytecode的虚拟机 JVM并非专属于Java语言,只要生成的编译文件能匹 ...
- 0030 Java学习笔记-面向对象-垃圾回收、(强、软、弱、虚)引用
垃圾回收特点 垃圾:程序运行过程中,会为对象.数组等分配内存,运行过程中或结束后,这些对象可能就没用了,没有变量再指向它们,这时候,它们就成了垃圾,等着垃圾回收程序的回收再利用 Java的垃圾回收机制 ...
- 0028 Java学习笔记-面向对象-Lambda表达式
匿名内部类与Lambda表达式示例 下面代码来源于:0027 Java学习笔记-面向对象-(非静态.静态.局部.匿名)内部类 package testpack; public class Test1{ ...
- 0025 Java学习笔记-面向对象-final修饰符、不可变类
final关键字可以用于何处 修饰类:该类不可被继承 修饰变量:该变量一经初始化就不能被重新赋值,即使该值跟初始化的值相同或者指向同一个对象,也不可以 类变量: 实例变量: 形参: 注意可以修饰形参 ...
- [Java入门笔记] 面向对象编程基础(二):方法详解
什么是方法? 简介 在上一篇的blog中,我们知道了方法是类中的一个组成部分,是类或对象的行为特征的抽象. 无论是从语法和功能上来看,方法都有点类似与函数.但是,方法与传统的函数还是有着不同之处: 在 ...
- 《Java学习笔记(第8版)》学习指导
<Java学习笔记(第8版)>学习指导 目录 图书简况 学习指导 第一章 Java平台概论 第二章 从JDK到IDE 第三章 基础语法 第四章 认识对象 第五章 对象封装 第六章 继承与多 ...
随机推荐
- php导出数据到多个csv并打包压缩
1.不压缩直接下载 // 测试php导出大量数据到csv public function actionExportData() { // 设置不超时 set_time_limit(0); // 设置最 ...
- zabbix3.4配置第三方邮件报警
废话不多说,直接进入主题. 1.安装mailx [root@localhost ~]#yum install malix -y #yum安装malix [ ...
- java接口顺口溜
原创作品,转载请注明来源,这篇博客我也发到了我的csdnhttps://blog.csdn.net/suyues/article/details/103458086 接口 接口定义全局变量和抽象方法 ...
- php模板模式(template design)
没有写停止条件,所以会一直运行哟. <?php /* The template design pattern defines the program skeleton of an algorit ...
- 第02节-BLE协议各层的形象化理解
本篇博客根据韦大仙视频,整理所得. 先上框图: ATT层 从ATT开始看,在上篇博客讲的医院结构里面有个检验室,检验室可以得到各项结果,但是它并不知道这些结果代表什么含义.类比的在BLE协议栈里面,A ...
- python类定义的讲解
python是怎么定义类的,看了下面的文章大家就会了,不用多说,开始学习. 一.类定义: 复制代码代码如下: class <类名>: <语句> 类实例化后,可以使用其属性,实际 ...
- 第二章 linux不为人知的命令
文件和目录命令 cd /home 进入 home目录' cd ..返回上一级目录 pwd显示当前工作路径 ls查看目录中的文件 ls -l 显示文件和目录的详细资料(可简写为ll),后可跟具体文件名 ...
- 使用面向对象思想封装js(附实例)
平时在写js时应该用面向对象思想将每一组功能封装成一个模块,可实现模块间的高内聚低耦合.重用.结构清晰........... 如果页面中逻辑复杂.功能多,不使用模块封装是不可想象的,维护起来非常复杂. ...
- Excel-自定义单元格、填充柄
数据分析是指用适当的统计分析方法对收集来的大量数据进行分析, 提取有用信息和形成结论而对数据加以详细研究和概括总结的过程. 工具:EXCEL,sql,SAS,SPSS,R,Python.Hadoop. ...
- Pandas | 23 分类数据
通常实时的数据包括重复的文本列.例如:性别,国家和代码等特征总是重复的.这些是分类数据的例子. 分类变量只能采用有限的数量,而且通常是固定的数量.除了固定长度,分类数据可能有顺序,但不能执行数字操作. ...