JUnit 5和Mockito进行单元测试!
1. JUnit 5 基础
JUnit 5是最新的JUnit版本,它引入了许多新特性,包括更灵活的测试实例生命周期、参数化测试、更丰富的断言和假设等。
1.1 基本注解
@Test:标记一个方法为测试方法。
@BeforeEach:在每个测试方法之前执行。
@AfterEach:在每个测试方法之后执行。
@BeforeAll:在所有测试方法之前执行一次(必须是静态方法)。
@AfterAll:在所有测试方法之后执行一次(必须是静态方法)。
@DisplayName:定义测试类或测试方法的自定义名称。
@Nested:允许将测试类分组到更小的测试类中。
@ParameterizedTest:进行参数化测试。
1.2 断言(Assertions类)
Assertions 类是JUnit 5中用于断言的核心类,提供了一系列静态方法来验证测试条件是否满足预期。
assertEquals(expected, actual):验证两个对象是否相等。如果不等,测试失败。
assertTrue(boolean condition):验证条件是否为真。如果为假,测试失败。
assertFalse(boolean condition):验证条件是否为假。如果为真,测试失败。
assertNull(Object object):验证对象是否为null。如果不为null,测试失败。
assertNotNull(Object object):验证对象是否不为null。如果为null,测试失败。
assertThrows(Class<T> expectedType, Executable executable):验证执行executable是否抛出了expectedType类型的异常。如果没有抛出或抛出其他类型的异常,测试失败。
assertAll(Executable... executables):同时执行多个断言,如果所有断言都成功,则测试通过;如果任何一个断言失败,所有失败的断言都会被报告。
assertSame(expected, actual):验证两个对象是否为同一个对象(使用==比较)。如果不是,测试失败。
assertNotSame(unexpected, actual):验证两个对象是否不是同一个对象(使用==比较)。如果是,测试失败。
assertTimeout(Duration timeout, Executable executable):验证执行executable是否在给定的时间内完成。如果执行超时,测试失败。
1.3 Assumptions 类
Assumptions 类提供了基于某些条件判断是否执行测试的能力。如果假设失败(即条件不满足),当前测试会被跳过,而不是失败。以下是一些常用的Assumptions方法:
assumeTrue(boolean assumption):如果假设为真,则继续执行测试;如果假设为假,测试被跳过。
assumeFalse(boolean assumption):如果假设为假,则继续执行测试;如果假设为真,测试被跳过。
assumingThat(boolean assumption, Executable executable):如果假设为真,则执行给定的executable(可以是一个测试方法);无论假设结果如何,测试都会继续执行,但executable只在假设为真时执行。
2.Mockito 基础
Mockito是一个流行的Java mocking框架,用于在隔离环境中测试代码,通过模拟依赖来确保测试的独立性。
基本注解:
@Mock:创建一个模拟对象。
@InjectMocks:创建一个实例,其字段或构造器依赖将被@Mock注解的模拟对象自动注入。
@Spy:可以创建一个真实的对象,并在需要时对它的某些方法进行模拟。
@Captor:用于捕获方法调用的参数。
常用方法:
mock(Class<T> classToMock):创建一个类的模拟对象。这是创建模拟对象的基础。
when(T methodCall):当你想模拟一个方法调用的返回值时使用。与thenReturn一起使用,可以指定一个方法调用应该返回什么值。
thenReturn(T value):与when方法一起使用,用于指定方法调用的返回值。
doReturn(Object toBeReturned):一个替代thenReturn的方法,用在当你需要模拟void方法或在spy对象上进行模拟时。
verify(T mock):用于验证某个模拟对象的某个方法是否被调用,以及调用的次数。
any():在设定模拟行为(如when)或验证(如verify)时,用于表示任何类型和值的参数。
eq(T value):用于指定方法调用时期望的具体参数值。
doNothing():用于模拟void方法时,指定该方法不执行任何操作。
doThrow(Throwable... toBeThrown):用于模拟方法调用时抛出异常。
spy(T object):创建一个真实对象的“间谍”或“spy”。这允许你在真实对象上“监视”方法调用,同时还能够覆盖某些方法的行为。
ArgumentCaptor<T>:用于捕获方法调用时传递的参数,以便后续进行断言。
times(int wantedNumberOfInvocations):与verify方法一起使用,用于指定某个方法被调用的具体次数。
never():与verify一起使用,用于验证某个方法从未被调用过。
示例
假设我们有一个PaymentService类,它依赖于PaymentProcessor接口:
public class PaymentService {private PaymentProcessor processor;public PaymentService(PaymentProcessor processor) {this.processor = processor;}public boolean process(double amount) {return processor.processPayment(amount);}}
下面是如何使用JUnit 5和Mockito来测试PaymentService类:
import org.junit.jupiter.api.Test;import org.junit.jupiter.api.extension.ExtendWith;import org.mockito.Mock;import org.mockito.junit.jupiter.MockitoExtension;import static org.mockito.Mockito.*;import static org.junit.jupiter.api.Assertions.*;@ExtendWith(MockitoExtension.class)public class PaymentServiceTest {@MockPaymentProcessor processor;@Testpublic void testProcessPayment() {// 设置PaymentService service = new PaymentService(processor);double amount = 100.0;when(processor.processPayment(amount)).thenReturn(true);// 执行boolean result = service.process(amount);// 验证assertTrue(result);verify(processor).processPayment(amount);}}
在这个例子中,我们使用@Mock来创建PaymentProcessor的模拟对象,并使用when(...).thenReturn(...)来定义当调用processPayment方法时应返回的值。然后,我们执行process方法,并使用assertTrue来验证结果是否符合预期。最后,我们使用verify来确认processPayment方法是否被正确调用。
3.JUnit 5 进阶用法
参数化测试(Parameterized Tests)
参数化测试允许你使用不同的参数多次运行同一个测试。这对于需要验证多种输入条件的方法特别有用。
@ParameterizedTest@ValueSource(strings = {"Hello", "JUnit"})void withValueSource(String word) {assertNotNull(word);}
动态测试(Dynamic Tests)
JUnit 5允许你动态生成测试,这些测试可以在运行时根据代码逻辑来决定。
@TestFactoryCollection<DynamicTest> dynamicTests() {return Arrays.asList(dynamicTest("Add test", () -> assertEquals(2, Math.addExact(1, 1))),dynamicTest("Multiply Test", () -> assertEquals(4, Math.multiplyExact(2, 2))));}
嵌套测试(Nested Tests)
使用@Nested注解,你可以将相关的测试组织在一起作为一个组在外层测试类中运行。
@Nestedclass WhenNew {@Testvoid isEmpty() {assertEquals(0, new ArrayList<>().size());}@Nestedclass AfterAddingAnElement {@Testvoid isNotEmpty() {List<Object> list = new ArrayList<>();list.add(new Object());assertEquals(1, list.size());}}}
超时测试
JUnit 5允许你为测试设置超时时间,确保测试在给定时间内完成。如果超出指定时间,测试将失败。
@Test@Timeout(value = 500, unit = TimeUnit.MILLISECONDS)void timeoutTest() {// 模拟一个耗时的操作// 如果操作超过500毫秒,则测试失败}
重复测试
如果你想对一个测试方法进行多次执行以确保其稳定性或寻找潜在的偶发问题,可以使用@RepeatedTest注解。
@RepeatedTest(5)void repeatTest() {// 这个测试会运行5次}
条件执行
JUnit 5提供了多种条件执行测试的方法,这些方法可以基于不同的条件来决定是否执行某个测试,例如操作系统类型、环境变量或Java版本。
@Test@EnabledOnOs(OS.WINDOWS)void onlyOnWindows() {// 仅在Windows操作系统上运行}@Test@EnabledIfSystemProperty(named = "user.name", matches = "yourUserName")void onlyForSpecificUser() {// 仅当系统用户名匹配时运行}
4.Mockito 进阶用法
使用@Spy进行部分模拟
有时你可能需要模拟类的某些方法,而保持其他方法的实际行为。@Spy注解允许你这样做。
@SpyList<String> spyList = new ArrayList<>();@Testvoid testSpy() {spyList.add("one");spyList.add("two");verify(spyList).add("one");verify(spyList).add("two");assertEquals(2, spyList.size()); // 实际调用方法// 修改方法行为doReturn(100).when(spyList).size();assertEquals(100, spyList.size()); // 方法行为被改变}
参数捕获(Argument Captors)
有时在验证方法调用时,你可能对方法调用的具体参数值感兴趣。@Captor注解和ArgumentCaptor类允许你捕获和检查这些值。
@MockList<String> mockList;@CaptorArgumentCaptor<String> argCaptor;@Testvoid argumentCaptorTest() {mockList.add("one");verify(mockList).add(argCaptor.capture());assertEquals("one", argCaptor.getValue());}
连续调用的不同返回值
有时候,你可能需要一个方法在连续调用时返回不同的值。Mockito允许你通过thenReturn()方法链来实现这一点。
when(mockList.size()).thenReturn(0).thenReturn(1);assertEquals(0, mockList.size());assertEquals(1, mockList.size());
验证调用次数
验证一个方法被调用了特定次数。
mockList.add("once");mockList.add("twice");mockList.add("twice");verify(mockList).add("once");verify(mockList, times(2)).add("twice");verify(mockList, never()).add("never happened");
模拟静态方法(需要Mockito 3.4.0及以上版本)
从Mockito 3.4.0开始,你可以使用mockStatic来模拟静态方法。这是通过try-with-resources语句来实现的,以确保静态mock在使用后被正确关闭。
try (MockedStatic<UtilityClass> mockedStatic = mockStatic(UtilityClass.class)) {mockedStatic.when(UtilityClass::someStaticMethod).thenReturn("mocked response");assertEquals("mocked response", UtilityClass.someStaticMethod());// 静态方法被模拟期间的行为}// 在这个块之外,静态方法恢复原有行为
模拟final方法和类
Mockito 2.x开始支持模拟final方法和类。为了启用这个功能,你需要在src/test/resources/mockito-extensions目录下创建一个名为org.mockito.plugins.MockMaker的文件,并在文件中添加一行内容:
mock-maker-inline
这样配置后,Mockito就可以模拟final类和方法了。
使用BDDMockito进行行为驱动开发
BDDMockito提供了一种基于行为驱动开发(BDD)的语法来编写Mockito测试,使得测试更加可读。
@Testvoid bddStyleTest() {// 给定BDDMockito.given(mockList.size()).willReturn(2);// 当int size = mockList.size();// 那么BDDMockito.then(mockList).should().size();assertEquals(2, size);}
5. @Mock 、@InjectMocks的原理
@Mock
· 原理:@Mock注解告诉Mockito框架为标注的字段生成一个模拟对象。这个模拟对象是动态生成的代理对象,它拦截对任何非final方法的调用,并允许测试者通过Mockito的API来配置这些调用的行为(例如返回特定的值或抛出异常)。
· 如何工作:当测试初始化时(例如,通过使用MockitoAnnotations.initMocks(this)方法或JUnit 5的@ExtendWith(MockitoExtension.class)),Mockito会扫描测试类中所有使用@Mock注解的字段,并为它们创建模拟对象。这些模拟对象默认不执行任何实际的代码逻辑,它们的行为完全由测试者通过Mockito的API来控制。
@InjectMocks
· 原理:@InjectMocks注解用于自动将@Mock(或@Spy)注解创建的模拟对象注入到被注解的字段中。Mockito会尝试通过构造器注入、属性注入或setter方法注入的方式,将模拟对象注入到@InjectMocks标注的实例中。
· 如何工作:
构造器注入:Mockito首先尝试使用包含最多参数的构造器来创建实例。如果构造器的参数能够与已声明的模拟对象匹配,这些模拟对象将被用作构造器参数。
属性注入:如果构造器注入不适用或不成功,Mockito会尝试直接设置实例中与模拟对象类型相匹配的属性。
Setter注入:最后,如果属性注入不成功,Mockito会尝试通过调用匹配的setter方法来注入模拟对象。
最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走!

软件测试面试文档
我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。


相关文章:
JUnit 5和Mockito进行单元测试!
1. JUnit 5 基础 JUnit 5是最新的JUnit版本,它引入了许多新特性,包括更灵活的测试实例生命周期、参数化测试、更丰富的断言和假设等。 1.1 基本注解 Test:标记一个方法为测试方法。 BeforeEach:在每个测试方法之前执行。 AfterEac…...
LeetCode 算法:完全平方数 c++
原题链接🔗:完全平方数难度:中等⭐️⭐️ 题目 给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。 完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的…...
深入CSS 布局——WEB开发系列29
CSS 页面布局技术允许我们拾取网页中的元素,并且控制它们相对正常布局流、周边元素、父容器或者主视口/窗口的位置。 一、正常布局流(Normal Flow) CSS的布局基础是“正常流”,也就是页面元素在没有特别指定布局方式时的默认排列…...
视频的容器格式和编码格式详解
视频的容器格式和编码格式是视频文件的两个核心概念,它们相互关联但具有不同的功能。以下是详细的解释: 1. 容器格式 (Container Format) 容器格式,又称封装格式,指的是视频文件的外壳或容器,它用于封装视频、音频、…...
Elasticsearch Mapping 详解
1 概述 映射的基本概念 Mapping 也称之为映射,定义了 ES 的索引结构、字段类型、分词器等属性,是索引必不可少的组成部分。 ES 中的 mapping 有点类似与DB中“表结构”的概念,在 MySQL 中,表结构里包含了字段名称,字…...
WPF 利用视觉树获取指定名称对象、指定类型对象、以及判断是否有验证错误
1.利用视觉树获取指定名称对象 /// <summary> /// Finds a Child of a given item in the visual tree. /// </summary> /// <param name"parent">A direct parent of the queried item.</param> /// <typeparam name"T">T…...
了解`re`模块的`split()`, `sub()`, `subn()`方法的作用
在Python中,re模块(即正则表达式模块)提供了强大的字符串处理能力,允许你通过模式匹配来执行复杂的文本搜索、替换和分割等操作。其中,split(), sub(), 和 subn() 方法是re模块中非常实用的几个函数,它们各…...
机器学习交通流量预测实现方案
机器学习交通流量预测实现方案 实现方案 1. 数据预处理 2. 模型选择 3. 模型训练与评估 代码实现 代码解释 小结 🎈边走、边悟🎈迟早会好 交通流量预测是机器学习在智能交通系统中的典型应用,通常用于预测道路上的车辆流量、速度和拥…...
QNN:基于QNN+example重构之后的yolov8det部署
QNN是高通发布的神经网络推理引擎,是SNPE的升级版,其主要功能是: 完成从Pytorch/TensorFlow/Keras/Onnx等神经网络框架到高通计算平台的模型转换; 完成模型的低比特量化(int8),使其能够运行在高…...
Redis实战宝典:开发规范与最佳实践
目录标题 Key命名设计:可读性、可管理性、简介性Value设计:拒绝大key控制Key的生命周期:设定过期时间时间复杂度为O(n)的命令需要注意N的数量禁用命令:KEYS、FLUSHDB、FLUSHALL等不推荐使用事务删除大key设置合理的内存淘汰策略使…...
RPC的实现原理架构
RPC(Remote Procedure Call,远程过程调用)是一种允许程序调用位于不同地址空间或网络上的函数或方法的技术,尽管这些调用看起来像是本地调用。RPC 的实现极大地简化了分布式系统中的通信,避免了开发人员直接处理底层网…...
OpenXR Monado Hello_xr提交Frame
OpenXR Monado Hello_xr提交Frame @src/tests/hello_xr/openxr_program.cpp RenderFrame())xrWaitFrame(m_session, &frameWaitInfo, &frameState)xrBeginFrame(m_session, &frameBeginInfo)std::vector<XrCompositionLayerBaseHeader*> layers;std::vecto…...
huggingface快速下载模型及其配置
大家知道,每次进huggingface里面一个个手动下载文件然后再上传到我们的服务器是很麻烦的。其实huggingface提供了下载整个包的命令,很简单,如下: 1. 进入huggingface官网,随便搜索一个模型,点击右上角的三…...
虚幻5|不同骨骼受到不同伤害|小知识(2)
1.蓝图创建一个结构,B_BoneDamage 结构里添加一个浮点变量,表示伤害倍数 2.当我们创建了一个结构,就需要创建一个数据表格,数据表格可以选择对应的结构 不同骨骼不同倍数伤害,骨骼要对应骨骼网格体的名称 3.把我们br…...
达梦SQL 优化简介
目录 一、定位慢 SQL (一)开启跟踪日志记录 1.跟踪日志记录配置 (二)通过系统视图查看 1.SQL 记录配置 2.查询方式 二、SQL分析方法 (一)执行计划 1.概述 2.查看执行计划 (二&#x…...
题解:CF1070B Berkomnadzor
CF1070B Berkomnadzor 题解 解题思路 不难想到将 IP 地址转化为二进制后插入一个字典树中,转化后二进制的长度就是 x x x 的长度。我们需要记录每个串结尾的颜色,不妨设黑名单为 1 1 1,白名单为 0 0 0,初始时每个位置的颜色是…...
shell 学习笔记:数组
目录 1. 定义数组 2. 读取数组元素值 3. 关联数组 4. 在数组前加一个感叹号 ! 可以获取数组的所有键 5. 在数组前加一个井号 # 获取数组的长度 6. 数组初始化的时候,也可以用变量 7. 循环输出数组的方法 7.1 for循环输出 7.2 while循环输出 7.2.1 …...
计算机基础知识复习9.5
数据交换 电路交换:交换信息的两个主机之间简历专用通道,传输时延小,实时性强,效率低,无法纠正错误。 报文交换:信息拆分成小包(报文)大小无限制,有目的/源等信息提高利用率。有转…...
spark.sql
from pyspark.sql import SparkSession from pyspark.sql.functions import col, count, mean, rank, row_number, desc from pyspark.sql.window import Window from pyspark.sql.types import StructType, StructField, StringType, IntegerType# 初始化 SparkSession 对象 s…...
2024 数学建模高教社杯 国赛(A题)| “板凳龙”舞龙队 | 建模秘籍文章代码思路大全
铛铛!小秘籍来咯! 小秘籍团队独辟蹊径,运用等距螺线,多目标规划等强大工具,构建了这一题的详细解答哦! 为大家量身打造创新解决方案。小秘籍团队,始终引领着建模问题求解的风潮。 抓紧小秘籍&am…...
循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝
目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为:一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...
解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...
淘宝扭蛋机小程序系统开发:打造互动性强的购物平台
淘宝扭蛋机小程序系统的开发,旨在打造一个互动性强的购物平台,让用户在购物的同时,能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机,实现旋转、抽拉等动作,增…...
vue3 daterange正则踩坑
<el-form-item label"空置时间" prop"vacantTime"> <el-date-picker v-model"form.vacantTime" type"daterange" start-placeholder"开始日期" end-placeholder"结束日期" clearable :editable"fal…...
鸿蒙(HarmonyOS5)实现跳一跳小游戏
下面我将介绍如何使用鸿蒙的ArkUI框架,实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...
【UE5 C++】通过文件对话框获取选择文件的路径
目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 ,这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器,右键点击 .uproject 文件,选择 "Generate Visual Studio project files",重…...
智能职业发展系统:AI驱动的职业规划平台技术解析
智能职业发展系统:AI驱动的职业规划平台技术解析 引言:数字时代的职业革命 在当今瞬息万变的就业市场中,传统的职业规划方法已无法满足个人和企业的需求。据统计,全球每年有超过2亿人面临职业转型困境,而企业也因此遭…...
