2.1 JUnit 5 测试发现机制详解


JUnit 5 测试发现机制详解

JUnit 5 的测试发现机制是框架的核心功能之一,负责识别测试类、方法和其他可执行元素,并构建出可执行的测试计划。该机制通过模块化设计支持高度扩展性,允许开发者自定义测试发现规则。以下是其工作原理的详细解析:


一、测试发现的核心组件
组件作用
TestEngine定义测试引擎的接口,负责发现和执行特定类型的测试(如 Jupiter、Vintage)。
DiscoverySelector指定测试发现的来源(如类名、包名、方法名、URI 等)。
DiscoveryFilter过滤不需要的测试元素(如按标签、包名排除)。
TestDescriptor描述测试的层次结构(如测试类、方法、动态测试),形成树状结构。

二、测试发现的完整流程
1. 触发测试发现
  • 入口:通过 Launcher API 或构建工具(如 Maven Surefire)启动测试。
  • 请求构建:创建 LauncherDiscoveryRequest,包含 DiscoverySelectorDiscoveryFilter
    LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request().selectors(selectPackage("com.example.tests")).filters(includeClassNamePatterns(".*Test")).build();
    
2. 引擎发现测试
  • 调用 TestEngine:每个注册的 TestEngine(如 JupiterTestEngine)处理发现请求。
  • 生成 TestDescriptor:引擎解析测试类和方法,构建树状结构:
    EngineDescriptor (root)
    └─ ClassTestDescriptor (com.example.MyTest)├─ MethodTestDescriptor (test1)└─ MethodTestDescriptor (test2)
    
3. 过滤与修剪
  • 应用 DiscoveryFilter:根据标签、包名等过滤 TestDescriptor
  • 修剪无用节点:移除无测试方法的空类或无效分支。
4. 生成测试计划
  • TestPlan 结构:将 TestDescriptor 树转换为可执行的 TestPlan,供后续执行阶段使用。

三、关键源码解析
1. TestEngine 接口
  • 核心方法
    public interface TestEngine {// 发现测试并生成 TestDescriptorTestDescriptor discover(EngineDiscoveryRequest request, UniqueId uniqueId);// 执行测试void execute(ExecutionRequest request);
    }
    
  • 实现类
    • JupiterTestEngine:处理 @Test@ParameterizedTest 等 JUnit 5 注解。
    • VintageTestEngine:兼容 JUnit 4 的 RunnerTestSuite
2. DiscoverySelector 类型
类型作用示例
ClassSelector选择特定类selectClass(MyTest.class)
MethodSelector选择特定方法selectMethod("com.example.MyTest#test1")
PackageSelector选择包及其子包下的所有类selectPackage("com.example")
UriSelector通过 URI 选择测试资源(如文件、目录)selectUri("file:/path/to/tests")
3. TestDescriptor 树结构
  • 根节点EngineDescriptor,代表测试引擎。
  • 中间节点ClassTestDescriptor(测试类)、NestedClassTestDescriptor(嵌套类)。
  • 叶子节点MethodTestDescriptor(测试方法)、DynamicTestDescriptor(动态测试)。

四、扩展测试发现机制
1. 自定义 DiscoverySelector

实现 DiscoverySelector 接口,支持从数据库或配置文件加载测试:

public class DatabaseSelector implements DiscoverySelector {private final List<String> testClasses;public DatabaseSelector(List<String> testClasses) {this.testClasses = testClasses;}// 实现选择逻辑
}
2. 自定义 TestEngine

实现 TestEngine 接口,支持自定义测试类型(如基于 YAML 的测试):

public class YamlTestEngine implements TestEngine {@Overridepublic TestDescriptor discover(EngineDiscoveryRequest request, UniqueId uniqueId) {EngineDescriptor root = new EngineDescriptor(uniqueId, "YAML Engine");// 解析 YAML 文件,生成 TestDescriptorreturn root;}@Overridepublic void execute(ExecutionRequest request) {// 执行 YAML 测试}
}
3. 注册自定义组件

通过 ServiceLoaderLauncherConfig 注册扩展:

// META-INF/services/org.junit.platform.engine.TestEngine
com.example.YamlTestEngine

五、测试发现的优化策略
  1. 懒加载测试类
    避免在发现阶段加载所有类,延迟到执行时加载(通过 ClassSelector 动态解析)。

  2. 并行发现
    使用多线程并行扫描类路径,加快大型项目的测试发现速度。

  3. 缓存机制
    缓存已发现的测试结构,避免重复扫描(需监听类路径变化)。


六、示例:跟踪一个 @Test 方法的发现流程
1. 测试类定义
package com.example;import org.junit.jupiter.api.Test;class MyTest {@Testvoid test1() {}
}
2. 发现过程
  1. Launcher 构建请求:选择包 com.example,过滤类名匹配 .*Test
  2. JupiterTestEngine 处理请求
    • 扫描 com.example 包,找到 MyTest 类。
    • 解析 @Test 注解,生成 MethodTestDescriptor
  3. 构建 TestDescriptor
    EngineDescriptor (junit-jupiter)
    └─ ClassTestDescriptor (MyTest)└─ MethodTestDescriptor (test1)
    
3. 过滤与执行

应用标签过滤后,将 TestPlan 传递给 ExecutionListener 执行。


七、常见问题与调试
1. 测试未被发现
  • 检查点
    • 类/方法是否被正确注解(如 @Test)。
    • DiscoverySelector 是否覆盖目标类。
    • DiscoveryFilter 是否意外排除测试。
2. 调试发现流程
  • 启用日志:添加日志配置(如 Log4j)并设置 org.junit.platform.engineDEBUG 级别。
  • 断点调试:在 JupiterTestEngine.discover() 方法中设置断点,跟踪 TestDescriptor 构建过程。

八、总结

JUnit 5 的测试发现机制通过模块化设计实现了高度灵活性和扩展性:

  • 核心流程:由 TestEngine 驱动,通过 DiscoverySelectorDiscoveryFilter 控制发现范围。
  • 可扩展性:支持自定义引擎、选择器和过滤器,适应复杂测试需求。
  • 性能优化:通过懒加载、并行和缓存提升大型项目的测试发现效率。

理解这一机制有助于:

  • 定制测试框架:如集成外部测试定义(YAML、数据库)。
  • 优化测试计划:按需过滤和排序测试用例。
  • 深度调试:定位测试未被发现或错误执行的根本原因。

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

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

相关文章

阿里云百炼平台对接DeepSeek官方文档

目录 1、支持的模型 2、快速开始 2.1、OpenAI兼容 2.1.1、python示例代码 返回结果 2.1.2、Node.js示例代码 返回结果 2.1.3、HTTP示例代码 返回结果 2.2、DashScope 2.2.1、python示例代码 返回结果 2.2.2、java示例代码 返回结果 2.2.3、HTTP代码示例 返回结…

【深度强化学习】策略梯度算法:REINFORCE

策略梯度 强化学习算法进阶 Q-learning、DQN 及 DQN 改进算法都是基于价值&#xff08;value-based&#xff09;的方法&#xff0c;其中 Q-learning 是处理有限状态的算法&#xff0c;而 DQN 可以用来解决连续状态的问题。在强化学习中&#xff0c;除了基于值函数的方法&#…

DeepSeek接口联调(postman版)

第一步&#xff1a;获取API key 获取APIkeys链接https://platform.deepseek.com/api_keys 点击创建 API key 即可免费生成一个key值&#xff0c;别忘记保存。 第二步&#xff1a;找到deepseek官方接口文档 文档地址&#xff1a;https://api-docs.deepseek.com/zh-cn/ 第三步…

Sublime Text 3 中的 Pylinter 配置

在 Sublime Text 3 中配置 Pylinter&#xff08;如 pylint&#xff09;来进行 Python 代码静态分析&#xff0c;可以帮助你提升代码质量、检测潜在的错误、强制遵守编码标准等。为了在 Sublime Text 3 中配置 pylint&#xff0c;你需要确保 pylint 已安装&#xff0c;并设置好相…

LC-搜索二维矩阵II、相交链表、反转链表、回文链表、环形链表、环形链表ll

搜索二维矩阵II 方法&#xff1a;从右上角开始搜索 我们可以从矩阵的右上角开始进行搜索。如果当前元素 matrix[i][j] 等于 target&#xff0c;我们直接返回 true。如果 matrix[i][j] 大于 target&#xff0c;说明 target 只能出现在左边的列&#xff0c;所以我们将列指针向左…

支持列表拖拽嵌套,AI流式输出的多模态文档编辑器flowmix/docx: 全面升级

hi, 大家好, 我是徐小夕. 马上又到周五了, 最近也收到很多用户对 flowmix/docx 多模态文档编辑器的反馈&#xff0c;我们也做了一波新功能的升级&#xff0c;今天就和大家分享一下 flowmix/docx 多模态文档编辑器的最新更新. 演示地址: https://flowmix.turntip.cn/docx 以下是…

服务器中部署大模型DeepSeek-R1 | 本地部署DeepSeek-R1大模型 | deepseek-r1部署详细教程

0. 部署前的准备 首先我们需要足够算力的机器&#xff0c;这里我在vultr中租了有一张A16显卡一共16GB显存的服务器作为演示。部署的模型参数为14b的。如果需要部署满血版本671b的&#xff0c;需要更大的算力支持&#xff0c;这里由于是个人资金有限&#xff0c;就演示14b的部署…

Linux软件编程(1)

1.总述&#xff1a; 2.标准io与文件io 标准C库提供的一套文件操作接口&#xff1b; Linux内核为Linux操作系统提供的一套文件操作接口。 3.函数接口&#xff1a; 注意 &#xff1a;什么是文件流&#xff1f; 数据从文件流入和流出体现的字节流。 注意&#xff1a;od -c 文件…

网页五子棋——通用模块

目录 项目创建 通用功能模块 错误码 自定义异常类 CommonResult jackson 加密工具 项目创建 使用 idea 创建 SpringBoot 项目&#xff0c;并引入相关依赖&#xff1a; 配置 MyBatis&#xff1a; 编辑 application.yml&#xff1a; spring:datasource: # 数据库连接配…

【工具】在idea运行go后端

场景&#xff1a;从gitee仓库下载一个go语言前后端分离项目&#xff0c;想跑通前后端 ---------------------------------------------------------------------------------------------------------------------- 后端 1.下载插件 在idea的setting里面输入go&#xff0c;…

通达信如何导出以往的分时数据

1当天分时数据的导出 以梦网科技为例&#xff0c;在分笔交易上面右键&#xff0c;选择“放大”&#xff0c;放大后选择“选项”&#xff0c;选择“数据导出”&#xff0c;弹出界面中修改路径与文件名即可。 2以往数据的导出 以梦网科技为例&#xff0c;今天是2025年2月14号…

【面试题系列】Java 多线程面试题深度解析

本文涉及Java 多线程面试题&#xff0c;从基础到高级&#xff0c;希望对你有所帮助&#xff01; 一、基础概念类 1. 请简述 Java 中线程的几种状态及其转换条件 题目分析&#xff1a;这是多线程基础中的基础&#xff0c;考查对线程生命周期的理解&#xff0c;在多线程编程中&…

Java Virtual Machine(JVM)

JVM跨平台原理 跨平台&#xff1a;一次编译&#xff0c;到处运行 本质&#xff1a;不同操作系统上运行的JVM不一样&#xff0c;只需要把java程序编译成一份字节码文件&#xff0c;JVM执行不同的字节码文件。 Java是高级语言&#xff0c;提前编译一下&#xff08;变成字节码文件…

duckdb导出Excel和导出CSV速度测试

运行duckdb数据库 D:>duckdb v1.2.0 5f5512b827 Enter “.help” for usage hints. Connected to a transient in-memory database. Use “.open FILENAME” to reopen on a persistent database. 生成模拟数据&#xff0c;10个列&#xff0c;100万行数据&#xff1b; --…

TCP/IP参考模型和网络协议

由于国防部担心他们一些重要的主机、路由器和互联网关可能会突然崩溃&#xff0c;所以网络必须实现的另一目标是网络不受子网硬件损失的影响&#xff0c;已经建立的会话不会被取消&#xff0c;而且整个体系结构必须相当灵活。 TCP/IP是一组用于实现网络互连的通信协议。Interne…

uniapp商场之订单模块【订单列表】

文章目录 前言一、准备静态结构(分包)二、Tabs滑动切换1.Tabs文字渲染2.点文字高亮切换3.swiper滑动切换三、Tabs页面跳转高亮四、订单列表渲染1.封装列表组件2.订单状态父传子3.封装请求API4.准备请求参数5.初始化调用6.页面渲染五、订单支付1.页面条件渲染2.事件绑定前言 …

【教程】MySQL数据库学习笔记(七)——多表操作(持续更新)

写在前面&#xff1a; 如果文章对你有帮助&#xff0c;记得点赞关注加收藏一波&#xff0c;利于以后需要的时候复习&#xff0c;多谢支持&#xff01; 【MySQL数据库学习】系列文章 第一章 《认识与环境搭建》 第二章 《数据类型》 第三章 《数据定义语言DDL》 第四章 《数据操…

Mysql数据库

一.数据定义语言DDL 一.概述 DDL用于定义和管理数据库的结构 DDL关键字&#xff1a;1.CREATE; 2.ALTER; 3.DROP 二.SQL命名规定和规范 1.标识符命名规则 2.标识符命名规范 三.库管理 1. CREATE DATABASE 数据库名; 2. CREATE DATABASE IF NOT EXISTS 数据库名; 3. CREATE…

C++,STL容器适配器,priority_queue:优先队列深入解析

文章目录 一、容器概览与核心特性核心特性速览二、底层实现原理1. 二叉堆结构2. 容器适配器架构三、核心操作详解1. 容器初始化2. 元素操作接口3. 自定义优先队列四、实战应用场景1. 任务调度系统2. 合并K个有序链表五、性能优化策略1. 底层容器选择2. 批量建堆优化六、注意事项…

django上传文件

1、settings.py配置 # 静态文件配置 STATIC_URL /static/ STATICFILES_DIRS [BASE_DIR /static, ]上传文件 # 定义一个视图函数&#xff0c;该函数接收一个 request 参数 from django.shortcuts import render # 必备引入 import json from django.views.decorators.http i…