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 第三章 基础语法 第四章 认识对象 第五章 对象封装 第六章 继承与多 ...
随机推荐
- Python的诞生和各种解释器
一:Python的诞生 参考:https://www.jianshu.com/p/1cc1382e5e04 二:Python的各种解释器 参考:https://www.liaoxuefeng.co ...
- Zabbix监控服务器磁盘I/O
一.场景说明: 需要使用Zabbix监控服务器上各个磁盘的I/O使用率,当zabbix自身带的item无法满足我们的时候,则需自定义item. 包括: 磁盘读的次数 磁盘读的毫秒数 磁盘写的次 ...
- Linux用户环境配置文件
用户操作环境配置文件: 从/etc/skel目录复制过来 .bashrc 打开新终端 /etc/bashrc .bash_profile 用户登 ...
- JS里==和===区别
面试常问的一个基础问题 == 和 ===区别是什么? 这里简单描述下 "==="叫做严格运算符,"=="叫做相等运算符 而且 == 会对数据做隐式转换,而=== ...
- windows环境安装rabbitmq
(1)下载并安装 Eralng 如:otp_win64_20.2.exe (以管理员身份运行安装) (2)下载并安装rabbitmq 如:rabbitmq-server-3.7.4.exe.双击安装, ...
- 葫芦娃团队对火鸡堂、基于云的胜利冲锋队团队的Beta产品测试报告
Beta项目互测 课程名称:软件工程1916|W(福州大学) 作业要求:Beta阶段团队项目互评 团队名称:葫芦娃队 作业目标:Beta项目互测 一.火鸡堂团队产品测试 1.截图(推荐用动态gif图录 ...
- 百度快排发包python核心源码
本源码仅供测试,发包有风险,优化还是踏实的好!本代码是本人自己学习python练手作品! 附上代码: # -*- coding: utf-8 -*-from selenium import webd ...
- VMware下安装Ubuntu虚拟机
ubuntu系统是以桌面应用为主的.当下最火的linux操作系统,具有实用的界面,并且完全免费. 在Ubuntu的 Linux 世界里,已经不再只是简陋的界面+命令行,而是一款华丽时尚且无比实用的操作 ...
- IE6兼容性bug汇总
1.终极方法:条件注释 <!--[if lte IE 6]> 这段文字仅显示在 IE6及IE6以下版本. <![endif]--> <!--[if gte IE 6]&g ...
- Python 模块A
模块的四种形式 自定义模块,例如自己定义的common.py就叫common模块 内置模块:python解释器自带的,不需要安装 第三方模块:需要自己安装,13万个库,无所不能,写库(弄得更简单) 包 ...