Junit 集成测试
前言
现在作者说明一下,作者需要开发一个简单的Vue+Springboot前后端分离实验,想要尽量将测试的流程应用到这样的系统中。单元测试请见Junit单元测试_Joy T的博客-CSDN博客,而单元测试加上mock呢,最多也只能测试一下Service层的业务逻辑,对于数据访问层的代码,比如save/insert等等,用单元测试不是很到位。Junit+mock请见Mock简单应用_Joy T的博客-CSDN博客
首先因为这些数据访问层的层数几乎已经到底层,无法使用mock去模拟一个下层对象。其次,对于数据访问层,确实应该测试一下与数据库真实的连接了,这种接近于实际情况的交互还是使用集成测试会好一些。
集成测试与单元测试应用场景对比
对于何时应该进行单元测试和集成测试,确实存在一些争议和不同的做法。但大体上,以下是一个通常的建议和理由:
单元测试
- 业务逻辑层(例如,服务层、工具类等):这是单元测试最有价值的地方。你可以测试逻辑是否正确、边界条件是否得到处理、异常是否正确抛出等。
- 自定义工具或库:如果你开发了一些自定义的工具类或库函数,那么对它们进行单元测试是很有意义的。
- 复杂的数据转换或处理:如果你的代码需要进行复杂的数据转换或处理(例如,将数据从一种格式转换为另一种格式),那么单元测试可以帮助确保这些转换或处理是正确的。
集成测试
- 数据访问层(例如,使用MyBatis的Mapper):对于数据访问层,通常集成测试更有意义。你可能会想知道SQL查询是否正确、返回的数据是否符合预期、事务是否正确处理等。对于JavaWeb来说,数据访问层通常指Dao层;而对于Springboot来说,数据访问层就是Repository。
- 外部服务交互:当你的代码与外部服务(例如,其他的微服务、第三方API等)交互时,集成测试可以帮助确保这些交互的正确性和稳定性。
- 整体应用流程:测试应用的整体流程,确保各个组件、服务或模块之间的交互是正确的。
集成测试是不是就是接口测试?
-
集成测试:它关注的是多个组件或系统的部分(如两个模块、一个服务和一个数据库等)如何一起工作。例如,你可能有一个测试来确保你的
UserRepository能够正确地从数据库中检索数据。 -
接口测试:接口测试或API测试,特指测试软件的接口,确保它们正常工作、可靠并且满足其预期的功能。这些接口可能是HTTP REST API、SOAP web服务或任何其他类型的API。
简言之,所有的接口测试都可以看作是集成测试,但并非所有的集成测试都是接口测试。集成测试包括的领域更大。
集成测试实例
在Spring Boot应用中,我们通常使用@DataJpaTest来进行数据访问层的集成测试。
对UserRepository进行集成测试的示例
User.java (Entity)
@Entity
public class User {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String username;private String password;// getters, setters, constructors...
}
UserRepository.java
public interface UserRepository extends JpaRepository<User, Long> {Optional<User> findByUsername(String username);
}
UserRepositoryIntegrationTest.java (测试类)
@RunWith(SpringRunner.class)
@DataJpaTest
public class UserRepositoryIntegrationTest {@Autowiredprivate TestEntityManager entityManager;@Autowiredprivate UserRepository userRepository;@Testpublic void whenFindByUsername_thenReturnUser() {// givenUser john = new User("john", "123456");entityManager.persist(john);entityManager.flush();// whenOptional<User> found = userRepository.findByUsername(john.getUsername());// thenassertTrue(found.isPresent());assertEquals(found.get().getUsername(), john.getUsername());}// 其他相关的集成测试方法...
}
- 我们使用
@DataJpaTest来启动一个嵌入式数据库,并自动配置Spring Data JPA。 TestEntityManager提供了一种更为简单的方式来管理持久性上下文,并允许我们执行常见的数据库操作。
详解UserRepositoryIntegrationTest.java (测试类)
我们来逐步解释UserRepositoryIntegrationTest.java中的内容。
1. 基本设置
@RunWith(SpringRunner.class)
@DataJpaTest
@RunWith(SpringRunner.class):这个注解告诉JUnit使用Spring的测试运行器。这意味着我们可以在测试中利用Spring Boot的特性。
@DataJpaTest:这个注解专门为Spring Data JPA的测试提供支持。它会配置一个嵌入式数据库(默认是H2),并且会进行JPA的相关配置,使得我们可以直接测试与数据库的交互。这里的“集成”体现在我们实际与一个真实的数据库交互,而不是使用mock。
2. 属性和注入
@Autowired
private TestEntityManager entityManager;@Autowired
private UserRepository userRepository;
TestEntityManager:非常重要,这是专门为测试提供的实体管理器。它是JPA EntityManager的一个简化版本,用于数据库操作。我们可以使用它来添加、更新或删除测试数据。
UserRepository:这是我们要测试的Spring Data JPA repository。一个专门的固定的实体管理器,一个是我们要测试的Repository层。
3. 测试方法
@Test
public void whenFindByUsername_thenReturnUser() {// givenUser john = new User("john", "123456");entityManager.persist(john);entityManager.flush();// whenOptional<User> found = userRepository.findByUsername(john.getUsername());// thenassertTrue(found.isPresent());assertEquals(found.get().getUsername(), john.getUsername());
}
这个测试方法分为三个阶段:给定 (given),当 (when) 和那么 (then)。
给定 (given): 这里,我们创建了一个新的用户对象,并使用
TestEntityManager将其持久化到数据库中(persist&flush)。当 (when): 在这一步,我们试图通过
UserRepository的findByUsername方法根据用户名找到用户。那么 (then): 这是我们的断言阶段,我们检查从
UserRepository返回的值是否符合我们的预期。我们预期的是,当我们查询一个已经存在于数据库中的用户名时,UserRepository应该返回那个用户。
如果在这三个阶段中的任何一个阶段出现错误或异常,或者预期的结果与实际的结果不匹配,那么这个测试就会失败。
When 详解
// when
Optional<User> found = userRepository.findByUsername(john.getUsername());
Optional是Java 8引入的一个容器对象,它可能包含一个值,也可能不包含(即为空),就是表面意思:可选。其主要目的是提供一个明确的方式来处理null值的情况,避免NullPointerException,更加灵活,便于测试。
在我们讨论的上下文中,Optional存储的是单个User对象,不是数组,对象名为found。所以,found.get()返回的是单个User对象。
如果期望从数据库检索多个User对象,那么UserRepository的方法返回类型可能会是List<User>,而不是Optional<User>。例如:
List<User> findByUsername(String username);
在这种情况下,如果有多个与指定用户名匹配的用户,那么返回的列表将包含所有这些用户。你可以通过检查列表的大小或迭代列表中的每个用户来处理多个用户的情况。
但在大多数应用程序中,用户名通常是唯一的,所以通过用户名检索用户时,通常只返回一个用户。这也是为什么在很多情况下,你会看到
Optional<User>作为返回类型。
Then 详解
// thenassertTrue(found.isPresent());assertEquals(found.get().getUsername(), john.getUsername());
这两句代码是利用Java的Optional类以及JUnit的断言来验证测试的预期结果。这并不直接对应User类的方法,而是与UserRepository的方法以及Java的Optional类相关。
让我们详细分析:
assertTrue(found.isPresent());
这句代码使用的isPresent()方法是Optional类的方法。当我们使用Spring Data JPA的repository方法返回对象时,为了处理可能的null值(例如当对象不存在于数据库中时),通常会返回Optional<T>类型。Optional类有一个isPresent()方法,如果Optional内部包含一个非null值,它返回true,否则返回false,相当于isExist()。
所以,assertTrue(found.isPresent());这行代码的意思是:我们期望UserRepository返回一个包含用户的Optional。
assertEquals(found.get().getUsername(), john.getUsername());
-
found.get(): 这是Optional类的另一个方法,它返回Optional对象内部的值(如果存在的话)。 -
getUsername(): 这是我们的User类的方法,用于获取用户的用户名。
所以,assertEquals(found.get().getUsername(), john.getUsername());这行代码的意思是:我们期望从数据库中检索到的用户(found.get())的用户名与我们原始存储的用户john的用户名相匹配。
这样,结合两句断言,我们可以测试UserReposiroty中定义的FindByUsername()方法是否能够按照用户名去得到指定用户!
SelectAll()!
当我们想测试类似selectAll这样的方法,返回的确实是一个List,一般不使用Optional类对象作为承载者。为了进行集成测试,我们可以添加一些记录到数据库,然后调用selectAll方法,最后验证返回的列表是否包含预期的记录。
以下是一个简单的示例,用于测试selectAll方法:
@RunWith(SpringRunner.class)
@DataJpaTest
public class UserRepositoryIntegrationTest {@Autowiredprivate TestEntityManager entityManager;@Autowiredprivate UserRepository userRepository;@Testpublic void testSelectAll() {// Given: 添加一些预期的记录到数据库User john = new User();john.setUsername("john");john.setPassword("john123");entityManager.persist(john);User jane = new User();jane.setUsername("jane");jane.setPassword("jane123");entityManager.persist(jane);// When: 调用selectAll方法List<User> users = userRepository.selectAll();// Then: 验证返回的列表是否包含预期的记录assertNotNull(users);assertTrue(users.size() >= 2); // 因为你不确定测试数据库中是否有其他记录,所以我们检查是否至少有我们插入的记录// 更进一步的验证可以检查返回的用户是否真的是我们预期的那些用户assertTrue(users.stream().anyMatch(user -> user.getUsername().equals("john")));assertTrue(users.stream().anyMatch(user -> user.getUsername().equals("jane")));}
}
存储john&jane用户,调用selectAll()方法,使用断言验证selectAll查询到的信息有没有john&jane。
相关文章:
Junit 集成测试
前言 现在作者说明一下,作者需要开发一个简单的VueSpringboot前后端分离实验,想要尽量将测试的流程应用到这样的系统中。单元测试请见Junit单元测试_Joy T的博客-CSDN博客,而单元测试加上mock呢,最多也只能测试一下Service层的业…...
Orleans的成员管理和故障检测故障检测
Orleans的成员管理和故障检测故障检测 简介 Orleans框架是一个基于.NET平台的开源分布式系统框架,用于开发可扩展,高可用,高性能的云服务应用程序。它采用了Actor模型,将分布式系统中的各个节点抽象成为Actor,使得开…...
分类选择,最多五级
效果图,这种竖向的分类选择,每一列可以用不同的背景颜色 组件代码 <template><view class"toolTypeBox" :style"max-height:${maxHeight}"><block v-for"(item,index) in datalist"><block v-if&…...
ASP.NET framework升级core .NET 6.0
C# ASP.NET framework 升级core .NET 6.0 .NET 7.0 .NET 8.0 或者以上 .net framework、.net standard、.net core .net 5/6/7/8 区别_.net 6.0和.net framework的区别-CSDN博客 using System.Web.Http; using HttpPostAttribute Microsoft.AspNetCore.Mvc.HttpPostA…...
BootStrap-前端框架
资料:https://v3.bootcss.com/components/ BootStrap的概念(Web框架) Bootstrap,来自 Twitter,是目前很受欢迎的前端框架。Bootstrap 是基于 HTML、CSS、JavaScript 的,它简洁灵活,使得 Web 开发更加快捷。…...
解读 | 自动驾驶系统中的多视点三维目标检测网络
原创 | 文 BFT机器人 01 背景 多视角三维物体检测网络,用于实现自动驾驶场景高精度三维目标检测,该网络使用激光雷达点云和RGB图像进行感知融合,以预测定向的三维边界框,相比于现有技术,取得了显著的精度提升。同时现…...
C++ 用户学习 Python 的最佳方法
对于很多是一名计算机科学专业的学生而言,很多入门是学习的C和 C,可能熟悉非常基本的 python 语法,以及 C 中相当高级的数据结构。现在想深入学习Python的话,光看很多在线教程可能没法有较大的提升,这里有一些针对C用户…...
使用docker搭建drogon windows10,linux,mac下开发环境
2023年10月13日14:52:26 本机环境 Windows 10 专业版 22H2 操作内核19045.2965 如果直接在windows,linux,mac上直接搭建环境确实有一点难度,之前drogon官方并未提供官方镜像,现在有了docker镜像确实方便了,其实我是最…...
【RKNN】YOLO V5中pytorch2onnx,pytorch和onnx模型输出不一致,精度降低
在yolo v5训练的模型,转onnx,再转rknn后,测试发现: rknn模型,量化与非量化,相较于pytorch模型,测试精度都有降低onnx模型,相较于pytorch模型,测试精度也有降低ÿ…...
六分科技CEO李阳:精准定位助力汽车智能化普及
10月10日,2023四维图新用户大会在上海成功举办。大会现场,六分科技展示了基于PPP-RTK技术的“星璨”产品和软硬件一体化解决方案。同时在智能驾驶主题论坛上,六分科技CEO李阳受邀发表了以《精准定位助力汽车智能化普及》为主题的演讲。 高精度…...
信号完整性分析基础知识之有损传输线、上升时间衰减和材料特性(六):衰减和dB
线路中的损耗对信号的主要影响是当信号沿线路长度传播时幅度减小。如果将幅度为 V 的正弦波电压信号引入传输线,则其幅度将随着传输线向下移动而下降。图 9-16 显示了如果我们可以冻结时间并观察直线上存在的正弦波,则正弦波在不同位置可能会是什么样子。…...
吃鸡达人必备:分享顶级干货+作图工具推荐+账号安全查询!
吃鸡达人们,你们好!今天我来给大家介绍一些炙手可热的吃鸡话题,以及一些让你实力飙升的独家干货! 首先,让我们说一下如何提高自己的游戏战斗力。作为一名专业吃鸡行家,我将与你们分享一些顶级游戏作战干货&…...
帆软报表解决单元格不显示问题
前言 使用帆软报表设计器制作普通报表时、设计器界面经常有一根垂直的 “虚线”。一旦单元格超过这条 “虚线” ,那么真正打开报表就看不到这些列了。以下提供了简单的修正方法、欢迎大家讨论交流。 操作环境 设计器是帆软报表 9.0,操作系统是 Window…...
LeetCode讲解篇之138. 随机链表的复制
LeetCode讲解篇之138. 随机链表的复制 文章目录 LeetCode讲解篇之138. 随机链表的复制题目描述题解思路题解代码 题目描述 题解思路 先遍历一遍链表,用哈希表保存原始节点和克隆节点的映射关系,先只克隆节点的Val,然后再次遍历链表ÿ…...
主定理(简化版)
主定理(Master Theorem)是用于分析递归算法时间复杂度的一个重要工具。它适用于形式化定义的一类递归关系,通常采用分治策略解决问题的情况。 假设我们有一个递归算法,它将问题分解成 a a a 个子问题,每个子问题的规模…...
HTTP1.0和HTTP2.0的区别
相同点:所有的HTTP请求都要基于TCP连接。 HTTP1.0:每次发送请求时建立一个TCP连接,得到响应后,释放TCP连接。 HTP1.1:**相比于1.0,引入了Keep live,客户端得到响应后,不会立刻释放T…...
ARM资源记录《AI嵌入式系统:算法优化与实现》第八章(暂时用不到)
1.CMSIS的代码 书里给的5,https://github.com/ARM-software/CMSIS_5 现在有6了,https://github.com/ARM-software/CMSIS_6 这是官网的书,介绍cmsis函数的https://arm-software.github.io/CMSIS_5/Core/html/index.html 2.CMSIS介绍 Cort…...
微信小程序2
一,视图层 1.什么视图层 框架的视图层由 WXML 与 WXSS 编写,由组件来进行展示。 将逻辑层的数据反映成视图,同时将视图层的事件发送给逻辑层。 WXML(WeiXin Markup language) 用于描述页面的结构。 WXS(WeiXin Script) 是小程序的一套脚本语…...
G.711语音编解码器详解
语音编解码利用人听觉上的冗余对语音信息进行压缩从而达到节省带宽的目的。值得注意的是,本文说的是语音编解码器,也就Speech codec,而常用的还有另一种编解码器称作音频编解码器,英文是Audio codec,它们的区别如下。 以前在学校的时候研究了很多VoIP的编解码器从G.723到A…...
蓝桥杯每日一题2023.10.17
迷宫 - 蓝桥云课 (lanqiao.cn) 题目描述 样例: 01010101001011001001010110010110100100001000101010 00001000100000101010010000100000001001100110100101 01111011010010001000001101001011100011000000010000 0100000000101010001101000010100000101010101100…...
未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
Java毕业设计:WML信息查询与后端信息发布系统开发
JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发,实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构,服务器端使用Java Servlet处理请求,数据库采用MySQL存储信息࿰…...
处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
深度学习水论文:mamba+图像增强
🧀当前视觉领域对高效长序列建模需求激增,对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模,以及动态计算优势,在图像质量提升和细节恢复方面有难以替代的作用。 🧀因此短时间内,就有不…...
C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...
Kafka主题运维全指南:从基础配置到故障处理
#作者:张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1:主题删除失败。常见错误2:__consumer_offsets占用太多的磁盘。 主题日常管理 …...
Vue 模板语句的数据来源
🧩 Vue 模板语句的数据来源:全方位解析 Vue 模板(<template> 部分)中的表达式、指令绑定(如 v-bind, v-on)和插值({{ }})都在一个特定的作用域内求值。这个作用域由当前 组件…...
