JUnit 5与TestNG对比:Java测试框架怎么选
对比JUnit 5与TestNG在单元测试、参数化测试、分组、依赖关系、并行执行、扩展机制和测试套件管理方面的差异,并结合实际Java项目说明如何选择测试框架。
如果是新建的普通 Java 项目,特别是以单元测试为主的 Spring、Spring Boot 或常规后端项目,通常可以优先选择 JUnit 5。 如果测试中大量使用分组、测试方法依赖、XML 套件编排、复杂并行策略或数据驱动场景,TestNG 往往更加直接。 两者都能完成常见 Java 测试任务,所以选择时不能只看“哪个功能更多”,而应该看项目准备怎样组织和执行测试。
下面不单纯罗列功能,而是从实际开发中最容易影响选择的几个问题入手:测试代码怎么写、参数化测试有什么区别、 如何组织测试集、能不能控制执行顺序、并行测试是否方便,以及已有项目是否值得迁移。
一、先理解两者的定位差异
JUnit 5 并不只是一个新的 @Test 注解集合。它由 JUnit Platform、JUnit Jupiter 等部分组成。
开发者平时编写 JUnit 5 测试,主要使用的是 Jupiter 编程模型;底层 Platform 则负责发现和启动测试。
TestNG 的思路更偏向一个完整的测试执行框架。除了普通单元测试,它把测试分组、参数传递、方法依赖、 DataProvider、Suite 和并行执行等能力直接放进自己的测试模型中,因此在集成测试、接口测试和较复杂的自动化测试套件中比较方便。

二、最基本的测试代码其实差别不大
假设有一个非常简单的价格计算类:
public class PriceCalculator {
public int add(int price, int shipping) {
return price + shipping;
}
}使用 JUnit 5
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class PriceCalculatorTest {
@Test
void shouldAddPriceAndShipping() {
PriceCalculator calculator = new PriceCalculator();
int result = calculator.add(100, 20);
assertEquals(120, result);
}
}使用 TestNG
import org.testng.Assert;
import org.testng.annotations.Test;
public class PriceCalculatorTest {
@Test
public void shouldAddPriceAndShipping() {
PriceCalculator calculator = new PriceCalculator();
int result = calculator.add(100, 20);
Assert.assertEquals(result, 120);
}
}对普通单元测试而言,两者都很直观。不能因为 TestNG 的 @Test 可以设置更多属性,
就认为它一定更适合单元测试。单元测试最重要的是彼此独立、执行快速、结果稳定,很多高级执行功能实际上用不到。
三、参数化测试:两边都能做好,不必因此单独换框架
有一种常见说法是“需要数据驱动测试就应该使用 TestNG”。这个说法过去容易让人接受,
因为 TestNG 的 @DataProvider 很成熟,但现在如果只是普通参数化测试,JUnit 5 同样能够自然处理。
例如测试多组加法数据。
JUnit 5 参数化测试
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
class PriceCalculatorTest {
private final PriceCalculator calculator = new PriceCalculator();
@ParameterizedTest
@CsvSource({
"100, 20, 120",
"50, 10, 60",
"0, 15, 15"
})
void shouldAddPriceAndShipping(int price, int shipping, int expected) {
assertEquals(expected, calculator.add(price, shipping));
}
}TestNG DataProvider
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class PriceCalculatorTest {
private final PriceCalculator calculator = new PriceCalculator();
@DataProvider(name = "priceData")
public Object[][] priceData() {
return new Object[][] {
{100, 20, 120},
{50, 10, 60},
{0, 15, 15}
};
}
@Test(dataProvider = "priceData")
public void shouldAddPriceAndShipping(
int price,
int shipping,
int expected) {
Assert.assertEquals(
calculator.add(price, shipping),
expected
);
}
}如果数据就在代码附近,JUnit 5 的 @CsvSource、@ValueSource 等写法通常很紧凑;
如果测试数据需要由代码动态构造,TestNG 的 @DataProvider 也很自然。
JUnit 5 同样可以通过方法或自定义参数源提供复杂数据,因此“是否需要参数化测试”本身并不足以决定框架。
四、分组测试:JUnit 5用Tag,TestNG用Group
实际项目经常需要区分快速单元测试、数据库集成测试和耗时测试。例如 CI 中每次提交只运行快速测试, 夜间任务再运行完整测试集。
JUnit 5 可以使用 @Tag:
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
class OrderServiceTest {
@Test
@Tag("fast")
void calculateTotal() {
// 快速测试
}
@Test
@Tag("integration")
void saveOrderToDatabase() {
// 集成测试
}
}TestNG 则直接通过 groups 设置:
import org.testng.annotations.Test;
public class OrderServiceTest {
@Test(groups = "fast")
public void calculateTotal() {
// 快速测试
}
@Test(groups = "integration")
public void saveOrderToDatabase() {
// 集成测试
}
}两种方法都能过滤测试。TestNG 的 group 与 Suite、依赖关系等执行配置结合得比较紧密; JUnit 5 的 Tag 则与 JUnit Platform 的发现和过滤机制结合。
五、TestNG真正有特点的地方:测试依赖关系
TestNG 可以直接描述“测试 B 依赖测试 A”。例如某个接口自动化测试需要先创建用户,再查询这个用户:
import org.testng.annotations.Test;
public class UserApiTest {
@Test
public void createUser() {
// 创建用户
}
@Test(dependsOnMethods = "createUser")
public void queryUser() {
// createUser 成功后再执行
}
}如果 createUser() 失败,依赖它的测试可以被跳过。TestNG 还支持基于 group 建立依赖。
JUnit 5 可以控制测试顺序,也能通过生命周期方法完成初始化,但它并没有把 dependsOnMethods 这种测试方法依赖关系作为核心测试模型。
这正是两者设计思路上的一个明显区别。

六、并行测试:两者都支持,但配置思路不同
“TestNG 能并行、JUnit 只能串行”也是一个需要修正的说法。JUnit Jupiter 已经支持并行执行, 只是默认仍采用顺序执行,需要显式开启。TestNG 同样提供多种并行方式,并且 Suite 层面的配置比较直观。
JUnit 5开启并行执行
可以在测试资源目录的 junit-platform.properties 中配置:
junit.jupiter.execution.parallel.enabled=true junit.jupiter.execution.parallel.mode.default=concurrent
实际项目开启并行后,还需要检查测试是否共享静态变量、数据库记录、临时文件、端口或者其他外部资源。 测试框架允许并行,并不代表现有测试代码天然线程安全。
TestNG通过Suite控制并行方式
例如下面的 testng.xml 以测试类为并行单位:
TestNG 可以围绕 methods、classes、tests、instances 等不同粒度安排并行执行。 对测试平台或者自动化测试工程来说,这种显式的 Suite 编排方式很实用。
七、扩展能力:JUnit 5的Extension模型更值得关注
JUnit 5 提供统一的 Extension 扩展机制,可以在测试生命周期、参数解析、条件执行等位置接入自己的逻辑。
常见写法是通过 @ExtendWith 注册扩展。:contentReference[oaicite:2]{index=2}
这也是为什么在很多以单元测试和框架集成为主的 Java 项目中,JUnit 5 用起来比较自然。 测试框架、Mock 工具以及应用框架可以通过扩展机制参与测试生命周期,而业务测试本身仍保持比较简单。
TestNG 则拥有 Listener 等扩展接口,并且自身执行模型提供了很多可配置点。如果你建设的是一个测试平台, 希望大量控制执行前后事件、结果处理、重试以及 Suite 行为,同样有较大的扩展空间。
八、用Maven或Gradle时怎么配置
下面使用具体版本演示依赖写法。示例采用 JUnit 5.14.4 和 TestNG 7.12.0; 如果项目已经由 Spring Boot BOM、公司统一 BOM 或其他依赖管理机制控制版本,应优先遵守项目自己的依赖管理, 不要为了照搬示例强制覆盖版本。JUnit 5.14.4 于 2026 年发布,TestNG 官方仓库目前也已发布 7.12.0。
Maven使用JUnit 5
org.junit.jupiter junit-jupiter 5.14.4 test
在项目根目录运行:
mvn test
Maven使用TestNG
org.testng testng 7.12.0 test
同样执行:
mvn test
Gradle使用JUnit 5
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter:5.14.4")
}
test {
useJUnitPlatform()
}Gradle使用TestNG
dependencies {
testImplementation("org.testng:testng:7.12.0")
}
test {
useTestNG()
}
九、几个常见项目应该怎么选
场景1:新建Spring Boot后端项目
主要需求是 Service、Controller、Repository 等常规测试,同时会使用 Mock、Spring Test 等工具。 这种项目通常直接使用 JUnit 5 更省事。
Spring Boot 的测试依赖体系本身对 JUnit Jupiter 有直接支持。如果项目由 Spring Boot 管理 JUnit 版本, 一般也没有必要另外导入 JUnit BOM 再覆盖一遍版本。
场景2:大量接口自动化测试
如果测试需要按照环境拆 Suite,需要执行 smoke、regression、production-check 等 group, 又存在登录、初始化数据、执行操作、清理环境等明显的流程关系,TestNG 会比较顺手。
场景3:大量参数组合测试
不要只因为“数据很多”就默认选择 TestNG。JUnit 5 的参数化测试已经能够处理多种参数来源。 如果项目其他部分更适合 JUnit 5,没有必要只为了 DataProvider 风格而更换框架。
场景4:需要高并发运行大量测试
两者都可以考虑。真正决定效果的通常不是框架名字,而是测试是否线程安全、外部环境能承受多少并发、 数据能否隔离以及 CI 机器有多少资源。
如果团队希望通过 Suite 明确控制 methods、classes、tests 等并行粒度,TestNG 的配置方式比较直接; 如果项目已经全面采用 JUnit Jupiter,也完全可以继续使用 JUnit 5 的并行执行能力。
场景5:旧项目已经有数千个TestNG测试
不建议仅仅因为新项目更常见 JUnit 5,就立即全部迁移。测试代码迁移不仅是修改 import, 还可能涉及生命周期方法、Listener、Group、DataProvider、Suite、执行顺序以及 CI 配置。
只有当现有 TestNG 方案已经明显影响维护,或者项目准备统一到 JUnit Platform 生态时, 才值得评估分阶段迁移。
十、JUnit 5和TestNG能不能同时存在
技术上可以,但“能同时使用”和“应该长期混用”是两个问题。
如果项目正在迁移,保留一部分 TestNG 测试,同时新增 JUnit 5 测试是可以理解的。 但如果没有明确原因,长期让开发人员在两个框架之间切换,会产生额外的认知成本: 生命周期注解不同、断言参数习惯可能不同、分组方式不同、执行配置也不同。
因此更合理的方式通常是确定一个主框架。另一个框架只在迁移阶段或确实存在特殊测试需求时使用。
十一、核心能力对比
| 比较项目 | JUnit 5 | TestNG |
|---|---|---|
| 普通单元测试 | 非常适合 | 非常适合 |
| 参数化测试 | @ParameterizedTest及多种参数源 | @DataProvider |
| 测试分类 | @Tag | groups |
| 方法之间建立依赖 | 不是核心测试模型 | 原生支持dependsOnMethods、dependsOnGroups |
| 并行执行 | 支持,需要配置执行模式 | 支持,Suite级配置较直接 |
| 测试套件 | JUnit Platform提供套件与过滤机制 | testng.xml能力比较突出 |
| 扩展方式 | 统一Extension模型 | Listener及其他扩展接口 |
| 嵌套组织测试 | @Nested表达能力很好 | 通常按类、Group和Suite组织 |
| 复杂测试流程编排 | 通常保持测试独立或借助外围工具 | Group、Dependency、Suite组合更直接 |
| 适合的新项目类型 | 普通Java、Spring、Spring Boot单元/集成测试 | 复杂自动化测试、流程测试、测试平台 |

十二、怎么做最终决定
如果现在面对的是一个没有历史包袱的新 Java 项目,可以先问自己三个问题。
测试是不是以相互独立的单元测试和常规集成测试为主? 如果是,JUnit 5 通常就是简单而稳妥的选择。
是否真的需要测试之间的依赖关系、复杂 Group、XML Suite 和多层执行编排? 如果这些能力会频繁出现,TestNG 的优势才会明显。
项目是不是已经形成了稳定的测试体系? 如果已有大量 JUnit 5 或 TestNG 测试,不要仅因为另一个框架某项功能更漂亮就整体迁移。
两个框架之间并不存在简单的“谁淘汰谁”。JUnit 5 已经具备参数化、标签过滤、扩展和并行测试等成熟能力; TestNG 的价值也并不只是 DataProvider,而在于它把分组、依赖、Suite 和执行策略放在了测试模型的核心位置。 只要根据项目真正的测试组织方式选择,而不是根据单个注解或一张功能数量表决定,通常就不会选错。
文中涉及的 JUnit 5 并行执行、Tag、Extension、当前 5.x 版本以及 TestNG Group、DataProvider、 Dependency、并行执行和当前版本信息,依据 JUnit 与 TestNG 官方文档及官方发布记录核对。
AirBuddy 3 是面向 macOS 的蓝牙外设增强工具,可集中查看 AirPods、Beats 与苹果设备电量,提供快速连接、播放控制、电量提醒、桌面小组件和多台 Mac 外设接力等功能。
AirServer macOS版可把 Mac 转换为无线投屏接收端,通过 AirPlay 或 Google Cast 接收手机、平板和电脑画面,并支持多设备同时连接、高清镜像及会话录制,适合家庭娱乐、课堂教学、会议演示和应用展示。
AnyDesk macOS版是一款适用于远程办公、设备维护和客户支持的远程桌面工具,可连接并操作异地计算机,支持文件传输、无人值守访问、会话权限控制及跨平台协作。
AirServer Windows版可把电脑变成AirPlay、Google Cast和Miracast无线投屏接收端,适合会议演示、课堂教学及家庭大屏分享。发送设备通常无需另装客户端,并可同时接收和智能排列多个镜像会话。
AlwaysUp是一款面向Windows的系统服务注册与程序托管工具,可让普通应用、脚本及批处理在无人登录时随系统启动,并通过状态检测、异常重启、资源监控和邮件通知保障关键任务持续运行。










