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 {
@Mock
PaymentProcessor processor;
@Test
public 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允许你动态生成测试,这些测试可以在运行时根据代码逻辑来决定。
@TestFactory
Collection<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注解,你可以将相关的测试组织在一起作为一个组在外层测试类中运行。
@Nested
class WhenNew {
@Test
void isEmpty() {
assertEquals(0, new ArrayList<>().size());
}
@Nested
class AfterAddingAnElement {
@Test
void 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注解允许你这样做。
@Spy
List<String> spyList = new ArrayList<>();
@Test
void 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类允许你捕获和检查这些值。
@Mock
List<String> mockList;
@Captor
ArgumentCaptor<String> argCaptor;
@Test
void 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测试,使得测试更加可读。
@Test
void 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…...

微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...

【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...

热门Chrome扩展程序存在明文传输风险,用户隐私安全受威胁
赛门铁克威胁猎手团队最新报告披露,数款拥有数百万活跃用户的Chrome扩展程序正在通过未加密的HTTP连接静默泄露用户敏感数据,严重威胁用户隐私安全。 知名扩展程序存在明文传输风险 尽管宣称提供安全浏览、数据分析或便捷界面等功能,但SEMR…...

Java数组Arrays操作全攻略
Arrays类的概述 Java中的Arrays类位于java.util包中,提供了一系列静态方法用于操作数组(如排序、搜索、填充、比较等)。这些方法适用于基本类型数组和对象数组。 常用成员方法及代码示例 排序(sort) 对数组进行升序…...
跨平台商品数据接口的标准化与规范化发展路径:淘宝京东拼多多的最新实践
在电商行业蓬勃发展的当下,多平台运营已成为众多商家的必然选择。然而,不同电商平台在商品数据接口方面存在差异,导致商家在跨平台运营时面临诸多挑战,如数据对接困难、运营效率低下、用户体验不一致等。跨平台商品数据接口的标准…...