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…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)
考察一般的三次多项式,以r为参数: p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]; 此多项式的根为: 尽管看起来这个多项式是特殊的,其实一般的三次多项式都是可以通过线性变换化为这个形式…...
HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散
前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说,在叠衣服的过程中,我会带着团队对比各种模型、方法、策略,毕竟针对各个场景始终寻找更优的解决方案,是我个人和我司「七月在线」的职责之一 且个人认为,…...
[论文阅读]TrustRAG: Enhancing Robustness and Trustworthiness in RAG
TrustRAG: Enhancing Robustness and Trustworthiness in RAG [2501.00879] TrustRAG: Enhancing Robustness and Trustworthiness in Retrieval-Augmented Generation 代码:HuichiZhou/TrustRAG: Code for "TrustRAG: Enhancing Robustness and Trustworthin…...
数据结构:递归的种类(Types of Recursion)
目录 尾递归(Tail Recursion) 什么是 Loop(循环)? 复杂度分析 头递归(Head Recursion) 树形递归(Tree Recursion) 线性递归(Linear Recursion)…...
