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 第三章 基础语法 第四章 认识对象 第五章 对象封装 第六章 继承与多 ...
随机推荐
- hadoop hdfs 有内网、公网ip后,本地调试访问不了集群解决
问题背景: 使用云上的虚拟环境搭建测试集群,导入一些数据,在本地idea做些debug调试,但是发现本地idea连接不上测试环境 集群内部配置hosts映射是内网映射(内网ip与主机名映射),本地只能 ...
- 页面预加载loading动画,再载入内容
默认情况下如果网站请求速度慢,所以会有一段时间的空白页面等等,用户体验效果不好,见到很多的页面都有预加载的效果,加载之前先加载一个动画,后台进程继续加载页面内容,当页面内容加载完之后再退出动画显示内容 ...
- PHP的SOLID设计原则
SOLID Design Principles, 这是一个比设计模式更高级别的概念, 以构建良好代码为目标,真正掌握了就是大师级别了. 我~~~仅知晓~ /*SOLID Design Principl ...
- JMeter基础【第三篇】JMeter5.1元件作用域及执行顺序
执行顺序,大家可以实践验证,加深印象. 最后,给大家说一个万能且保险的方法:放到对应的取样器下面即可.
- Java 静态、类加载
1.静态是什么?有什么用? static的主要作用在于创建独立于具体对象的域变量或者方法. 每创建一个对象,都会在堆里开辟内存,存成员(属性),但是不存方法,方法是共用的,没必要每一个对象都浪费内存去 ...
- 【CSP-S膜你考】我们的可可西里
我们的可可西里 题面 转眼到了2008年的6月9日,盼望已久的高考结束了.我们踏上了向西的旅程(本来是想写西去之路,可是考虑不太妥当).可可西里,多么诱人的名词,充满了奇幻的色彩和自然的淳朴.从可可西 ...
- 使用kubernetes管理包的常用命令
常用命令是: ## 获取指定命名空间(rubikt)下所有的部署的服务 kubectl.exe get deployments --namespace rubikt ## 获取制定命名空间(rubik ...
- SCDM导入点数据
我们有时候需要把外部的点导入SCDM当中,但是SCDM没有像ICEM或者DM那样直接提供点导入的选项,是不是SCDM就无法导入点的数据了呢?答案当然是否定的.把点导入SCDM中的方法总结如下(示例数据 ...
- linux 自动化安装系统 -KS文件
# Kickstart file automatically generated by anaconda. installharddrive --partition=sdb2 --dir=lang e ...
- 一个web请求的全过程
参考文档:http://www.mamicode.com/info-detail-1357508.html 名词解释DNS: DNS(Domain Name System,域名系统),因特网上作为域名 ...