Mock工具之Mockito实战

article/2025/9/15 12:05:04

在实际项目中写单元测试的过程中我们会发现需要测试的类有很多依赖,这些依赖项又会有依赖,导致在单元测试代码里几乎无法完成构建,尤其是当依赖项尚未构建完成时会导致单元测试无法进行。为了解决这类问题我们引入了Mock的概念,简单的说就是模拟这些需要构建的类或者资源,提供给需要测试的对象使用。业内的Mock工具有很多,也已经很成熟了,这里我们将直接使用最流行的Mockito进行实战演练,完成mockito教程。

一、Mock工具概述

1、mock

 

EasyMock 以及 Mockito 都因为可以极大地简化单元测试的书写过程而被许多人应用在自己的工作中,但是这两种 Mock 工具都不可以实现对静态函数、构造函数、私有函数、Final 函数以及系统函数的模拟,但是这些方法往往是我们在大型系统中需要的功能。

另外,关于更多Mockito2.0新特性,参考官方介绍文档,里边有关于为什么不mock private的原因,挺有意思的:

https://github.com/mockito/mockito/wiki/What%27s-new-in-Mockito-2

2、Mockito准备工作

###Maven###

通过Maven管理的,需要在项目的Pom.xml中增加如下的依赖:

<dependencies><dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>2.7.19</version><scope>test</scope></dependency></dependencies>

在程序中可以import org.mockito.Mockito,然后调用它的static方法。

Maven用户可以声明对mockito-core的依赖。 Mockito自动发布到Bintray的中心,并同步到Maven Central Repository。

特别提醒:使用手工依赖关系管理的Legacy构建可以使用1. *“mockito-all”分发。 它可以从Mockito的Bintray存储库或Bintray的中心下载。 在但是Mockito 2. * “mockito-all”发行已经停止,Mockito 2以上版本使用“mockito-core”。

官网下载中心:

Maven Central Repository Search

目前最新版本为2.7.19,由于公司网络网关问题,最好是去官网手工下载。

另外Mockito需要Junit配合使用,在Pom文件中同样引入:

<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency>

 然后为了使代码更简洁,最好在测试类中导入静态资源,还有为了使用常用的junit关键字,也要引入junit的两个类Before和Test:

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

3、模拟对象

创建 Mock 对象的语法为 mock(class or interface)。

Mock 对象的创建

mock(Class classToMock);mock(Class classToMock, String name)mock(Class classToMock, Answer defaultAnswer)mock(Class classToMock, MockSettings mockSettings)mock(Class classToMock, ReturnValues returnValues)

 可以对类和接口进行mock对象的创建,创建时可以为mock对象命名。对mock对象命名的好处是调试的时候容易辨认mock对象。

Mock对象的期望行为和返回值设定

假设我们创建了LinkedList类的mock对象:

 LinkedList mockedList = mock(LinkedList.class);

4、设置对象调用的预期返回值

通过 when(mock.someMethod()).thenReturn(value) 来设定 Mock 对象某个方法调用时的返回值。我们可以看看源码中关于thenReturn方法的注释:

使用when(mock.someMethod()).thenThrow(new RuntimeException) 的方式来设定当调用某个方法时抛出的异常。

 以及Answer:

Answer 是个泛型接口。到调用发生时将执行这个回调,通过  Object[] args = invocation.getArguments();可以拿到调用时传入的参数,通过 Object mock = invocation.getMock();可以拿到mock对象。

有些方法可能接口的参数为一个Listener参数,如果我们使用Answer打桩,我们就可以获取这个Listener,并且在Answer函数中执行对应的回调函数,这对我们了解函数的内部执行过成有很大的帮助。

使用doThrow(new RuntimeException(“clear exception”)).when(mockedList).clear();mockedList.clear();的方式Mock没有返回值类型的函数:

 

 

doThrow(new RuntimeException()).when(mockedList).clear();

//将会 抛出 RuntimeException:

mockedList.clear();

这个实例表示当执行到mockedList.clear()时,将会抛出RuntimeException。其他的doXXX执行与它类似。

例如 : doReturn()|doThrow()| doAnswer()|doNothing()|doCallRealMethod() 系列方法。

Spy函数:

你可以为真实对象创建一个监控(spy)对象,当你使用这个spy对象时,真实的对象也会被调用,除非它的函数被打桩。你应该尽量少的使用spy对象,使用时也需要小心,例如spy对象可以用来处理遗留代码,Spy示例如下:

List list = new LinkedList();//监控一个真实对象List spy = spy(list);//你可以为某些函数打桩when(spy.size()).thenReturn(100);//使用这个将调用真实对象的函数spy.add("one");spy.add("two");//打印"one"System.out.println(spy.get(0));//size() 将打印100System.out.println(spy.size());//交互验证verify(spy).add("one"); 
verify(spy).add("two");

理解监控真实对象非常重要,有时,在监控对象上使用when(Object)来进行打桩是不可能或者不切实际的。因为,当使用监控对象时,请考虑用doReturn、Answer、Throw()函数组来进行打桩,例如:

List list = new LinkedList();

List spy = spy(list);

//这是不可能的: 因为调用spy.get(0)时会调用真实对象的get(0)函数,此时会发生

//IndexOutOfBoundsException异常,因为真实对象是空的 when(spy.get(0)).thenReturn("foo");

//你需要使用 doReturn() 来打桩

doReturn("foo").when(spy).get(0);

Mockito并不会为真实的对象代理函数调用,实际上它会复制真实对象,因此,如果你保留了真实对象并且与之交互,不要期望监控对象得到正确的结果。当你在监控对象上调用一个没有stub函数时,并不会调用真实对象的对应函数,你不会在真实对象上看到任何效果。

5、验证被测试类方法

Mock 对象一旦建立便会自动记录自己的交互行为,所以我们可以有选择的对它的 交互行为进行验证。在 Mockito 中验证 Mock 对象交互行为的方法是 verify(mock).someMethod(…)。最后 Assert() 验证返回值是否和预期一样。

 

6、Demo

 从网上找来一个最简单的代码实例,下面以具体代码演示如何使用Mockito,代码有三个类,分别如下:

Person类:

public class Person {private final int id; private final String name; public Person(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public String getName() { return name; }
}

PersonDao类:

public interface PersonDao {Person getPerson(int id); boolean update(Person person); }

 PersonService类:

public class PersonService {private final PersonDao personDao; public PersonService(PersonDao personDao) { this.personDao = personDao; } public boolean update(int id, String name) { Person person = personDao.getPerson(id); if (person == null) { return false; } Person personUpdate = new Person(person.getId(), name); return personDao.update(personUpdate); }
}

仍然使用Junit自动生成测试类或者手工新建测试类:

测试代码生成后,将默认assertfail的删掉,输入以下两个测试方法:

import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;public class PersonServiceTest {private PersonDao     mockDao;private PersonService personService;@Beforepublic void setUp() throws Exception {//模拟PersonDao对象mockDao = mock(PersonDao.class);when(mockDao.getPerson(1)).thenReturn(new Person(1, "Person1"));when(mockDao.update(isA(Person.class))).thenReturn(true);personService = new PersonService(mockDao);}@Testpublic void testUpdate() throws Exception {boolean result = personService.update(1, "new name");assertTrue("must true", result);//验证是否执行过一次getPerson(1)verify(mockDao, times(1)).getPerson(eq(1));//验证是否执行过一次updateverify(mockDao, times(1)).update(isA(Person.class));}@Testpublic void testUpdateNotFind() throws Exception {boolean result = personService.update(2, "new name");assertFalse("must true", result);//验证是否执行过一次getPerson(1)verify(mockDao, times(1)).getPerson(eq(1));//验证是否执行过一次updateverify(mockDao, never()).update(isA(Person.class));}
}

 

注意:我们对PersonDAO进行mock,并且设置stubbing,stubbing设置如下:

  • 当getPerson方法传入1的时候,返回一个Person对象,否则默认返回空
  • 当调update方法的时候,返回true

这里使用了两个参数匹配器:

  isA():Object argument that implements the given class.

  eq():int argument that is equal to the given value

注:Mockito使用verify去校验方法是否被调用,然后使用isA和eq这些内置的参数匹配器可以更加灵活,

关于参数匹配器的详细使用请参考官网文档:https://static.javadoc.io/org.mockito/mockito-core/2.25.0/org/mockito/ArgumentMatchers.html

由于官网的代码和解释非常详细,此处就不再赘述。

仍然调用Junit执行单元测试代码,结果如图所示:

验证了两种情况:

  • 更新id为1的Person的名字,预期:能在DAO中找到Person并更新成功
  • 更新id为2的Person的名字,预期:不能在DAO中找到Person,更新失败

这里也可以查看Eclipse抛出的异常信息:

Argument(s) are different! Wanted:

personDao.getPerson(1);

-> at PersonServiceTest.testUpdateNotFind(PersonServiceTest.java:41)

Actual invocation has different arguments:

personDao.getPerson(2);

-> at PersonService.update(PersonService.java:8)

二、单元测试与覆盖率

1、Junit 2、JaCoCo 3、EclEmma

2 覆盖率

覆盖率如下图显示:

 

覆盖率仍然使用JaCoCo和EclEmma:

l  未覆盖代码标记为红色

l  已覆盖代码会标记为绿色

l  部分覆盖的代码标记为黄色

颜色也可以在Eclipse中自定义设置:

 

 

 在Eclipse下方的状态栏窗口,有一栏“Coverage”,点击可以显示详细的代码覆盖率:

如何导出为Html格式的Report:

在Eclipse下方的Coverage栏鼠标右键选择“Export Session…”,在弹出窗口中选择export的目标为“Coverage Report”如下图:

 

点击“Next”按钮后,在接下来的弹出窗口选择需要导出的session,Format

类型选择“HTML report”,导出位置暂时选择为桌面,都选择之后点击“Finish”按钮就生成好了。

 

在桌面上找到一个叫做index.html的页面就是刚刚生成好的Coverage Report:

点击文件夹可以进入目录,进一步查看子文件的覆盖率:

 

 


http://chatgpt.dhexx.cn/article/f0I7ck2S.shtml

相关文章

Mockito 入门

目录 1.什么是 Mock 测试&#xff1f;2.Mockito简介3.在 SpringBoot 单元测试中使用 Mockito3.1 Maven依赖&#xff1a;3.2 UserService.java3.3 User.java3.4 thenReturn系列方法&#xff08;测试桩&#xff09;3.5 thenThrow系列方法3.6 verify 系列方法 4.Spring中mock任何容…

Mock工具之Moco使用教程

目录 一、什么是Moco二、安装&配置三、接口配置&测试3.1 第一个简单的请求&#xff1a;3.2 Get请求&#xff1a;3.3 Post请求&#xff1a;3.4 返回值为Json格式的请求&#xff1a;3.5 带cookie信息才能访问的post请求&#xff1a;3.6 重定向请求&#xff1a;3.7 前后端…

mock功能

目标 了解mock的作用及使用场景&#xff1b; mock使用场景 mock&#xff1a;假的 前端程序员提到的mock数据的含义是&#xff1a;真的假数据 真的&#xff1a;符合接口规范要求的。 假数据&#xff1a;数据是人为创建出来的&#xff0c;不是真正的业务数据。 什么时候需…

mock的介绍

1.什么是mock mock测试是以可控的方式模拟真实的对象行为。程序员通常创造模拟对象来测试对象本身该具备的行为&#xff0c;很类似汽车设计者使用碰撞测试假人来模拟车辆碰撞中人的动态行为 2.为什么要使用mock 之所以使用mock测试&#xff0c;是因为真实场景很难实现或者短…

Mock介绍

mock的定义&#xff08;what&#xff09;&#xff1a; mock是在测试过程中&#xff0c;对于一些不容易构造/获取的对象&#xff0c;创建一个mock对象来模拟对象的行为 为什么要使用mock&#xff08;why&#xff09;&#xff1a; 在做单元测试过程中&#xff0c;经常会有以下的…

Mockito单元测试

文章目录 Mockito单元测试为什么要使用Mock?导入依赖import导入包使用Mock模拟测试某个类中的某个方法是否可以成功执行使用Mock模拟某个类的方法&#xff0c;自己给这个方法返回我们指定的值使用Mock模拟某个方法调用后会抛出指定的异常使用Mock模拟测试某个类中的某个方法(加…

javascript 中怎么判断为数字类型

javascript中判断变量是否为数字类型抄的方法有两种&#xff1a; 方法一&#xff1a; function isnum(s) { if(s!null){ var r,re; re /\d*/i; //\d表示数字,*表示匹配多个数字 r s.match(re); return (rs)?true:false; } return false; } 方法二&#xff1a; function isnu…

js判断字符串是不是一个纯数字

1.使用隐式转换判断 使用parseInt()/parseFloat()会把其他类型转换为数值&#xff0c;转换结果为数值或NaN,转换字符串的时候&#xff0c;头部内容有数值就会截取前面&#xff0c;如123456aff789bb转成123456&#xff0c;后面即使有数字也不会转换处理&#xff0c;开头如果是字…

javaScript 判断是否为数值类型的三种方法

第一种方法:isNaN(value) isNaN : isNaN() 函数用于检查其参数是否是非数字值。 返回true则判断为非数值。缺点&#xff1a;空字符串&#xff0c;空格 &#xff0c;null会做为数字0进行处理&#xff0c;结果为falsehttps://www.w3school.com.cn/js/jsref_isnan.anusp 第二种方法…

JS中判断变量是否为数字方法

推荐教程&#xff1a;《JavaScript视频教程》 JavaScript 是一种动态类型语言&#xff0c;这意味着解释器在运行时确定变量的类型。实际上&#xff0c;这也允许我们在相同的代码中使用相同的变量来存储不同类型的数据。如果没有文档和一致性&#xff0c;我们在使用代码时并不总…

matlab绘制图形中,常用函数调用(num2str,disp,gcf,hold on,plot,axis,subplot,line,stairs,grid,set,gca)

num2str 将数字转换为字符数组 s = num2str(A) 将数字数组转换为表示数字的字符数组。输出格式取决于原始值的大小。 num2str对于使用数值标注和标题绘图非常有用。 s = num2str(A,precision) 返回一个字符数组,表示具有精度指定的最大有效位数的数字。 s = num2str(A,fo…

Matlab中num2str函数使用

目录 语法 说明 示例 浮点值的默认转换 指定精度 指定格式 num2str函数将数字转换为字符数组。 语法 s num2str(A)s num2str(A,precision)s num2str(A,formatSpec) 说明 s num2str(A) 将数值数组转换为表示数字的字符数组。输出格式取决于原始值的量级。num2str 对…

C/C++ split函数 num2str str2num函数

C/C split函数,num2str及str2num函数实现 在C98及其以下版本的C/C中&#xff0c;没有现成的字符串split函数和num2str及str2num函数&#xff0c;对于竞赛不能使用C11的我来说非常头疼&#xff0c;通过学习stringstream和strtok实现了自己的split函数&#xff0c;通过stringstr…

matlab 中num2str函数的使用

运行ECO代码报错&#xff1a;转义字符 ‘\d’ 无效。有关支持的特殊字符&#xff0c;请参阅 ‘doc sprintf’。 num2str函数功能&#xff1a;将数字转换为字符串 具体所有功能可见官网。 这里使用的格式为&#xff1a; str num2str(A, format) 将 format 指定的格式应用到 A …

js基础运用——js用*打印三角形,菱形

js基础运用 新手见解&#xff0c;有不足或更简单的方法希望各位指点 1.用*排列直角三形 &#xff08;图一&#xff09; &#xff08;图二&#xff09; 图一图二是js语句利用*号画出来的直角三角形&#xff0c;方法思路都一致&#xff0c;下面以图一为例讲一下本人所用的方法…

java:打印十字图

java:打印十字图 题目 问题描述 小明为某机构设计了一个十字型的徽标&#xff08;并非红十字会啊&#xff09;&#xff0c;如下所示&#xff1a;..$$$$$$$$$$$$$.. ..$...........$.. $$$.$$$$$$$$$.$$$ $...$.......$...$ $.$$$.$$$$$.$$$.$ $.$...$...$...$.$ $.$.$$$.$.$$…

JAVA 利用JFram,JPanel,JScrollPane简单文本框实现

****1.JFram 支持通用窗口的所有基本功能 2…JPanel是一个无边框&#xff0c;不可以被移动的&#xff0c;放大的&#xff0c;缩小的&#xff0c;或者关闭的面板&#xff0c;默认布局是FlowLayout&#xff08;也可用setLayout&#xff08;&#xff09;来设定&#xff09;** 3……

deflater java_Java Deflater finished()用法及代码示例

如果已到达压缩数据输出流的末尾&#xff0c;则java.util.zip中Deflater类的finished()函数将返回true。 函数签名&#xff1a; public boolean finished() 用法: d.finished(); 参数&#xff1a;该函数不需要参数 返回类型&#xff1a;该函数返回布尔值&#xff0c;即如果所有…

jQuery API ( 三 ) -------- 链式编程 与 修改样式方法

今天这篇文章是 jQuery API 的第三篇&#xff0c;今天心情很好&#xff0c;因为破了300粉&#xff0c;希望所有代码人前端人在编程与写作的路上都能一帆风顺&#xff0c;全都早日拿认证。okk言归正传&#xff0c;这篇文章将带大家走进链式编程的世界&#xff0c;这也是 jQuery …

jsfiddle 使用教程

最近有许多的Css 3 demo&#xff0c;因此为了方便查阅&#xff0c;就将demo部分放在 jsfiddle &#xff0c;方便日后翻阅。 这是 JSFIDDLE 的官网文档&#xff0c;都是英文&#xff0c;不过对照看还是可以的&#xff1a;官方文档 HTML区域&#xff1a; 它的HTML区域已经包含 ht…