Java 单元测试的框架选型其实没那么复杂:JUnit 5 是单测事实标准,TestNG 强在套件编排和并行,Mockito 负责把外部依赖挡在测试之外。大多数团队真正踩的坑不在于「用哪个」,而在于把环境初始化塞进每个方法、用 mock 替掉自己不拥有的类型、或者用测试去验证调用次数而不是行为。文末附速查表。
一、JUnit:Java 单测事实标准
JUnit 5 拆成 Platform / Jupiter / Vintage 三层:常用注解有 @Test、@ParameterizedTest、@ExtendWith。断言用 Assertions,生命周期用 @BeforeEach。嵌套测试用 @Nested 表达场景。JUnit 4 的 @RunWith 在迁移时用 Vintage 兼容。
几个容易踩的点
- 测实现细节。好测试要快、隔离、名字表达行为。不要测实现细节。
- 复制粘贴用例。参数化优于复制粘贴,@ParameterizedTest 比手写多份用例更稳。
- 把覆盖率当目标。覆盖率是辅助不是目标,别为了数字堆无意义的断言。
- Android 用错运行器。Android 上 JVM 单测用 Robolectric 或纯 Java 模块;仪器测走 AndroidJUnitRunner。
什么时候用:纯单模块单元测试默认 JUnit 5。CI 里失败即停还是聚合报告,看团队习惯。
二、TestNG:套件、依赖、分组与并行
TestNG 比 JUnit 4 更强调套件、依赖、分组和并行。它用 @Test、@BeforeMethod、@BeforeSuite 等注解描述测试方法,用 testng.xml 跨 class 编排 suite,数据驱动用 DataProvider,失败重试用 retry analyzer。软断言 SoftAssert 适合一批校验一起失败。和 Spring 集成用 AbstractTestNGSpringContextTests,Maven surefire 要指定 suiteXmlFile。很多接口自动化和测试平台选它,是因为一套 XML 就能把冒烟、回归和分组跑起来;和 JUnit 5 相比,依赖测试和灵活套件仍是它的长处。
三个容易踩的坑
- 环境初始化放进每个方法。生命周期按 suite、test、class、method 分层:BeforeSuite 放昂贵的环境准备,不要在每个方法里连一次数据库。
- 依赖链脆弱。dependsOnMethods 能串流程,但失败会跳过后续,稳定性不如显式编排。
- 并行串数据。thread-count 开起来后静态变量和共享 driver 会串数据,每个线程用自己的上下文;并行时注意线程安全的测试夹具。
什么时候用:接口自动化、多分组回归和需要 XML 编排的测试仓库适合 TestNG。纯单模块单测用 JUnit 5 更轻。接入时先写通一条带 DataProvider 的用例,再上并行和监听器报结果。
三、Mockito:用动态代理 / 字节码造假对象
用动态代理 / 字节码造假对象:mock、when / thenReturn、verify。spy 包装真实对象。参数匹配器 any() 要全用匹配器。静态方法在 mockito-inline 里用 mockStatic。ArgumentCaptor 拿回调参数。
几个容易踩的坑
- mock 自己不拥有的类型。不要 mock 你不拥有的类型(如 Android 框架),优先伪造自己的接口;能写假实现就别 mock。
- verifyNoMoreInteractions 太脆。它容易脆,别拿它当硬约束。
- Kotlin / Android 用错工具。Kotlin 项目可用 MockK;Android 仪器测里 Mockito 常需 dexmaker。
什么时候用:测试应验证行为而非调用次数。Kotlin 项目用 MockK 比硬套 Mockito 的 JVM 写法顺手;Android 仪器测记得配 dexmaker。
四、选型与用法边界:JUnit 5 还是 TestNG
单测用 JUnit 5,需要 XML 套件 / 依赖顺序 / 并行接口自动化用 TestNG,挡外部依赖用 Mockito。两者并非互斥——很多项目单测用 JUnit 5,集成测试仓库用 TestNG,Mockito 在两边都用。
- 已有 TestNG 生态或需要依赖测试顺序:留 TestNG。
- 新项目、纯单模块单测:更常见选 JUnit 5。
- mock 的底线:只 mock 自己拥有的接口,不拥有的类型去写 Fake;验证行为而不是调用次数。
附:Java 单元测试框架速查表
| 场景 | 用什么 | 不要用什么 |
|---|---|---|
| 纯单模块单元测试 | JUnit 5(@ParameterizedTest、@Nested) | 测实现细节、为覆盖率堆无意义断言 |
| 接口自动化 / 多分组回归 / XML 编排 | TestNG(DataProvider、testng.xml) | 把环境初始化放进每个 @Test、依赖链串太深 |
| 挡外部依赖 | Mockito(mock / spy / ArgumentCaptor) | mock 不拥有的类型、verifyNoMoreInteractions 当硬约束 |
| Kotlin 项目 | MockK | 硬套 Mockito 的 JVM 写法 |
| Android 仪器测 | Robolectric / AndroidJUnitRunner + dexmaker | 用错 JUnit 运行器、Mockito 不配 dexmaker |
Java 单元测试框架选型与用法:JUnit、TestNG 与 Mockito
https://lautung.com/archives/java-unit-testing
评论