Mockito新手使用教程

Mockito新手使用教程

背景

实在惭愧,干开发这么久了,从来没在单元测试用过mockito,倒不是没写单元测试的意思,只是用都是@SpringBootTest这类型的集成测试,有时候服务后台需要做mock接口,也是用的WireMock。第一次接触mockito,有点懵不清楚该怎么用,找了几个文章看了一下,都是教你那几个注解的含义,没有介绍使用的场景。

新手一定都有的疑问

  1. @Mock修饰的类型,他的调用方法都是虚拟的,并不是真正执行的,意义何在?
  2. 我怎么确保整个流程是正常通畅的?
  3. 有些流程确实要执行真正调用的,该怎么办?

概念

Mockito的主要功能和特点包括:

  1. 创建模拟对象:使用Mockito.mock(Class<T> classToMock)方法创建一个模拟对象。
  2. 设定行为:使用when(mock.method()).thenReturn(value)来设定当调用某个方法时返回特定的值。
  3. 验证行为:使用verify(mock).method()来验证某个方法是否被调用。
  4. 参数匹配器:使用ArgumentMatchers类来匹配参数,以便在设定行为和验证时更灵活。
  5. 注解支持:使用@Mock@InjectMocks注解来简化模拟对象的创建和注入。

mockito常用的注解

1. @Mock

用于创建和注入模拟对象。

@Mock
private SomeService someService;
  • @Mock 注解告诉 Mockito 创建一个 SomeService 类型的模拟对象,并将其赋值给 someService 变量。
  • 通常与 @RunWith(MockitoJUnitRunner.class)MockitoAnnotations.initMocks(this) 一起使用。

2. @InjectMocks

用于创建类的实例并注入模拟对象。

@InjectMocks
private SomeController someController;
  • @InjectMocks 注解告诉 Mockito 创建 SomeController 类的实例,并将用 @Mock 注解创建的模拟对象注入到该实例中。
  • 依赖注入的方式可以是构造函数注入、方法注入或字段注入。

3. @Spy

用于部分模拟对象,即部分真实部分模拟。

@Spy
private SomeService someService = new SomeService();
  • @Spy 注解告诉 Mockito 创建一个部分模拟对象,该对象在调用真实方法时将实际调用,而在调用模拟方法时则使用模拟行为。
  • 也可以使用 Mockito.spy() 方法来创建部分模拟对象。

4. @Captor

用于创建并自动注入 ArgumentCaptor,用于捕获方法调用时的参数。

@Captor
private ArgumentCaptor<SomeArgument> argumentCaptor;
  • @Captor 注解告诉 Mockito 创建一个 ArgumentCaptor 对象,并将其注入到 argumentCaptor 变量中。
  • ArgumentCaptor 可以用来捕获方法调用时的参数,以便在测试中进行验证。

实操

概念知识就只有这么一点,但是实际的场景使用该怎么用呢?像我们常用的开发场景,有controller、service、mapper等等,该怎么开展测试呢,又有哪些需要注意的地方?

场景举例

假设我需要通过id获取到用户的信息。

假设我们有以下层次结构:

  1. Controller层:处理HTTP请求。
  2. Service层:包含业务逻辑。
  3. Repository层(或Mapper层):与数据库交互。

实现代码

首先,我们定义Controller、Service和Repository层。

1. 用户模型
public class User {private Long id;private String name;private String email;// Constructors, getters, setters, etc.
}
2. Repository层
import org.springframework.stereotype.Repository;@Repository
public interface UserRepository {User findById(Long id);
}
3. Service层
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserService {@Autowiredprivate UserRepository userRepository;public User getUserById(Long id) {return userRepository.findById(id);}
}
4. Controller层
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;@RestController
public class UserController {@Autowiredprivate UserService userService;@GetMapping("/users/{id}")public ResponseEntity<User> getUserById(@PathVariable Long id) {User user = userService.getUserById(id);return ResponseEntity.ok(user);}
}

单元测试

使用Mockito来完成Service层和Controller层的单元测试。

1. Service层单元测试
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;public class UserServiceTest {@Mockprivate UserRepository userRepository;@InjectMocksprivate UserService userService;public UserServiceTest() {MockitoAnnotations.openMocks(this);}@Testpublic void testGetUserById() {User mockUser = new User(1L, "John Doe", "john.doe@example.com");when(userRepository.findById(1L)).thenReturn(mockUser);User user = userService.getUserById(1L);assertEquals(1L, user.getId());assertEquals("John Doe", user.getName());assertEquals("john.doe@example.com", user.getEmail());verify(userRepository).findById(1L);}
}
2. Controller层单元测试
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.http.MediaType;@WebMvcTest(UserController.class)
public class UserControllerTest {@Autowiredprivate MockMvc mockMvc;@MockBeanprivate UserService userService;@InjectMocksprivate UserController userController;@BeforeEachpublic void setUp() {MockitoAnnotations.openMocks(this);mockMvc = MockMvcBuilders.standaloneSetup(userController).build();}@Testpublic void testGetUserById() throws Exception {User mockUser = new User(1L, "John Doe", "john.doe@example.com");when(userService.getUserById(1L)).thenReturn(mockUser);mockMvc.perform(get("/users/1").contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andExpect(jsonPath("$.id").value(1L)).andExpect(jsonPath("$.name").value("John Doe")).andExpect(jsonPath("$.email").value("john.doe@example.com"));verify(userService).getUserById(1L);}
}

解释

  1. Service层测试:使用@Mock注解模拟UserRepository,并使用@InjectMocks注解注入到UserService中。通过when(...).thenReturn(...)设定模拟对象的行为,然后调用userService.getUserById方法并验证结果和行为。
  2. Controller层测试:使用@WebMvcTest注解来加载UserController,并使用@MockBean注解模拟UserService。通过mockMvc发送HTTP GET请求,并验证响应的状态和内容。

总结

单元测试的重点放在service层测试,因为service层是主要的业务逻辑整合。

@Mock注解只是用来注入到可能出现空指针的地方,所以@Mock注入的对象都不会得到真实的调用,比如当@Mock修饰UserRepository时,userRepository.findById()其实并没用真正查库去完成查询的,就算打断点也不会进入方法体。所以说白了@Mock就是用来修饰暂不关心的逻辑,但又要防止它空指针报错时用的。

@InjectMocks会真实创建一个该类的实例,调用方法也会进入到方法体,逐行执行。所以@InjectMocks是用来修饰主要要执行的逻辑,但是如果你的类继承了父类,并调用父类的方法,这会报空指针异常的。比如你使用了Mybatis-plus,你调用了userService.save(),会提示没有这个方法。这种时候就要使用@Spy来修饰UserService,随后调用就不会提示错误了,但是实际上并不会真的把数据存入到库里。

如果你在使用Mockito时需要真正从数据库中获得数据,你就得获得sqlSession对象,通过反射获得你的mapper类,再真实执行。

如果你需要测试接口的完整功能连通性,那还是得用@SpringbootTest或者通过接口调用去完成测试。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/22706.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot修真秘籍

一、古代篇 在修仙界中&#xff0c;Spring Boot 自动装配之术&#xff0c;犹如炼器炉中的异火&#xff0c;能够将繁杂的配置化作一缕轻烟&#xff0c;让修仙者在构建应用之路上&#xff0c;如履平地&#xff0c;轻松驾驭。 第一章&#xff1a;灵根觉醒——Spring Boot 的诞生 …

基于单片机的电子万年历设计

摘要: 本设计以 AT89C51 单片机为主控器,使用 DS1302 时钟芯片、DS18B20 温度芯片、LCD1602 显示模块,利用Proteus 仿真软件和 Keil 编译软件进行了基于单片机的电子万年历仿真,设计的万年历可以在液晶上显示时间,同时还具有时间校准、温度显示等功能。 关键词 :单片机…

【方法论】钟澄国家杰青 鲸吞法 写文献综述、大论文框架必备

Xlab2020的个人空间-Xlab2020个人主页-哔哩哔哩视频 老师指南 就是1号文件有了后&#xff0c;也就是【】概括了之后&#xff0c;我们会运行下程序&#xff08;程序我会让学生share&#xff09;。然后程序会执行这么一个操作&#xff0c;复制下【】内容&#xff0c;然后在第二个…

SpringBoot: 使用GraalVM编译native应用

曾今Go语言里让我最艳羡的两个特性&#xff0c;一个是Goroutine&#xff0c;一个是native编译。 Java 21的虚线程实现了类似Goroutine的能力。Spring Boot 3.x开始提供了GraalVM的支持&#xff0c;现在Spring Boot也能打包成native文件了。 这一篇文章的目标是用一个案例讲解如…

linux系统ubuntu中在命令行中打开图形界面的文件夹

在命令行中打开当前路径&#xff0c;以文件管理器的形式打开&#xff1a; 命令 # 打开文件管理器 当前的路径 nautilus .nautilus 是一个与 GNOME 桌面环境集成的文件管理器的命令行启动程序。在 Linux 系统中&#xff0c;特别是使用 GNOME 作为桌面环境时&#xff0c;用户经…

pidstat -d 1分析磁盘吞吐量

iostat -dx 1 查看磁盘IO吞吐量 pidstat -d 1看是哪个进程写的

水电站大坝安全监测工作详解

水电站大坝安全监测是确保大坝结构安全和操作安全的关键组成部分。本文将详细解释水电站大坝安全监测的9项主要工作内容&#xff0c;帮助理解其重要性和执行过程。 1) 现场监测 现场监测是水电站大坝安全监测的首要步骤。监测人员需要定期对大坝的物理结构进行检查&#xff0c;…

论文AI率不达标?AI工具助你一臂之力

告诉大家一个非常残忍的答案&#xff0c;以后所有论文都会被查ai率的。 学术界不仅关注传统的抄袭问题&#xff0c;还增加了一项名为“AIGC检测”的指标。例如知网、维普等平台都能检测论文AI率。 用GPT写论文虽然重复率基本不用担心&#xff0c;但是AI率基本都较高&#xff…

在 Qt Creator 上创建 ROS 项目并新建/导入 ROS 包

0、引言 ⚠️ 在开始之前&#xff0c;您需要确保您已经为 Qt 配置好了 ROS 开发环境了。如果您还没有配置好&#xff0c;可以参考这篇文章 本文将着手探讨如何在 Qt Creator 上编辑 ROS 项目&#xff08;工作空间&#xff09;。 1、本教程使用到的相关软件或产品 Ubuntu 20.0…

OpenCV学习(4.2) 图像的几何变换

1.目标 学习将不同的几何变换应用到图像上&#xff0c;如平移、旋转、仿射变换等。你会看到这些函数: cv.getPerspectiveTransform 2.缩放 缩放是调整图片的大小。 OpenCV 使用 cv.resize() 函数进行调整。可以手动指定图像的大小&#xff0c;也可以指定比例因子。可以使用不…

【Vue】小黑记事本

文章目录 一、需求说明二、思路分析三、代码实现 一、需求说明 拆分基础组件渲染待办任务添加任务删除任务底部合计 和 清空功能持久化存储 二、思路分析 拆分基础组件 咱们可以把小黑记事本原有的结构拆成三部分内容&#xff1a;头部&#xff08;TodoHeader&#xff09;、列…

基于semantic_kernel的ChatMD系统

问答系统需求文档 一、项目概述 本项目旨在开发一个能够上传 MD 文件&#xff0c;并基于 MD内容进行问答互动的系统。用户可以上传 MD文件&#xff0c;系统将解析 MD内容&#xff0c;并允许用户通过对话框进行问答互动&#xff0c;获取有关 MD文件内容的信息。 二、功能需求…

FreeRTOS学习笔记-基于stm32(8)信号量总结(二值信号量、计数型信号量、互斥信号量、优先级翻转、优先级继承)

一、什么是信号量 信号量是一种队列&#xff0c;用于任务间同步和资源管理的机制&#xff0c;主要用来传递状态。就像是一种特殊的“旗子”或“钥匙”&#xff0c;用来在不同的任务之间进行沟通和协调&#xff0c;确保它们能够正确地配合工作&#xff0c;不会互相干扰。 二、二…

保研面试408复习 8——计算机网络(浏览器http)、离散数学(平面图)、操作系统、数据结构

文章目录 一、计算机网络1、从在浏览器输入网址到页面显示的过程1. 输入网址2. DNS 解析3. 建立TCP连接4. 发送HTTP请求5. 服务器处理请求并响应6. 浏览器处理响应7. 页面渲染 二、离散数学一、平面图1、平面图性质2、Kuratowski定理 三、操作系统四、数据结构 一、计算机网络 …

IDCF五周年专场—【研发效能·创享大会】圆满落幕!

2024 年5 月25 日&#xff0c;【研发效能创享大会】—IDCF五周年专场在北京希尔顿欢朋酒店&#xff08;大红门&#xff09;成功举办&#xff01;本次大会旨在为社区成员提供一个学习与交流的平台&#xff0c;分享技术经验&#xff0c;交流行业见解&#xff0c;促进技术合作与创…

Java模板模式及代码

意图 在父类中定义了算法的骨架&#xff0c;而将一些步骤延迟到子类中&#xff0c;使得子类可以不改变一个算法的结构&#xff0c;就可以重定义该算法的某些特定步骤&#xff0c;这种类型的设计模式属于行为型模式。 主要解决的问题 解决在多个子类中重复实现相同的方法的问…

北京海淀区永丰产业基地地块出让,海开控股79.17亿元竞得

据悉&#xff0c;位于北京海淀区西北旺镇的永丰产业基地地块正式出让&#xff0c;引起了业界的广泛关注。经过激烈的竞争&#xff0c;最终由海开控股以79.17亿元的价格成功竞得该地块&#xff0c;成交楼面价达到35588元/平方米&#xff0c;溢价率为1.5%。此次出让的地块不仅吸引…

【Java基础】线程的五大状态

新建状态 使用 new 关键字和 Thread 类或其子类建立一个线程对象后&#xff0c;该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。 就绪状态 当线程对象调用了start()方法之后&#xff0c;该线程就进入就绪状态。就绪状态的线程处于就绪队列中&#xff…

景深技术在AI绘画中的魔法:为数字艺术注入新维度

引言&#xff1a; 在人工智能&#xff08;AI&#xff09;的浪潮中&#xff0c;绘画艺术领域迎来了革命性的变革。AI绘画不仅改变了创作过程&#xff0c;还为艺术家和设计师提供了前所未有的工具。其中&#xff0c;景深技术作为一种重要的视觉处理手段&#xff0c;在AI绘画中的应…

error:nullptr was not declared in this scope

nullptr是c11的内容,在Qt5.5.0中使用时需要在.pro添加 QMAKE_CXXFLAGS -stdc11