JUnit — 是Java和Kotlin生态系统中用于模块化测试的标准框架,在Android开发中用于在隔离组件级别检查业务逻辑。该框架提供了一套注解、断言方法和Test Runner,用于自动发现和执行测试。根据JUnit.org的数据,该库仍然是JVM生态系统中最流行的单元测试解决方案:超过70%的Java项目使用某个版本的JUnit。
要点概述
JUnit — 是一个开源框架,用于编写和运行Java和Kotlin语言的可重复单元测试。它属于xUnit生态系统——基于Kent Beck和Erich Gamma架构的框架家族,并且是Android Studio和IntelliJ IDEA中的标准测试工具。
JUnit的主要任务是将一小段代码(方法、类)隔离出来,并在可控条件下检查其行为。测试以带有注解的普通Java/Kotlin类形式编写,而框架负责发现测试、管理其生命周期以及收集执行统计信息。
JUnit的第一个版本于1997年发布,彻底改变了开发方式,推广了测试驱动开发(TDD)实践。如今,JUnit 5(Jupiter)是最新版本,已完全重新设计为模块化架构,支持Java 8+和扩展。
机制 JUnit的执行基于Test Runner模式,该模式扫描classpath,查找带有@Test注解的方法,创建测试类实例并按特定顺序运行方法。Test Runner管理生命周期:调用顺序为 BeforeAll → BeforeEach → Test → AfterEach → AfterAll。
JUnit将每个测试方法包装在单独的类实例中,这保证了测试之间的隔离性。这意味着类的字段不会在不同的@Test方法之间保持状态——每个测试都从一个干净的对象开始。
最简单的JUnit测试看起来像一个带有@Test注解的方法,方法内部调用被测代码并通过断言检查结果。如果断言未通过——测试被视为失败,JUnit会报告错误。
@Test
void additionShouldReturnCorrectSum() {
var calculator = new Calculator();
var result = calculator.add(2, 3);
assertEquals(5, result);
}
Test Runner会自动找到这样的方法,运行它并报告结果。如果方法没有抛出异常——测试通过(绿色)。如果断言失败——测试失败(红色)。
JUnit注解定义了测试代码何时以及如何执行。在JUnit 5中,注解位于org.junit.jupiter.api包中,涵盖所有阶段:数据准备、测试执行、资源清理。
| 注解 | 用途 | 执行时机 |
|---|---|---|
| @Test | 标记测试方法 | 每次调用一次 |
| @BeforeEach | 每个测试前准备 | 每个@Test之前 |
| @BeforeAll | 一次性类初始化 | 所有测试之前一次 |
| @AfterEach | 每个测试后清理 | 每个@Test之后 |
| @AfterAll | 一次性类结束 | 所有测试之后一次 |
| @DisplayName | 测试的可读名称 | 装饰 |
让我们看一个完整的测试类,具有正确的生命周期。setUp方法在每个测试之前创建一个新的Calculator实例,而tearDown释放资源——例如,关闭文件描述符或数据库连接。
class CalculatorTest {
private Calculator calculator;
@BeforeEach
void setUp() {
calculator = new Calculator();
}
@Test
void subtractionShouldReturnCorrectResult() {
int result = calculator.subtract(10, 4);
assertEquals(6, result);
}
@AfterEach
void tearDown() {
calculator.reset();
}
}
断言是静态方法,用于将实际结果与预期结果进行比较,在不匹配时抛出异常。JUnit 5提供了org.junit.jupiter.api.Assertions类,包含超过25种方法用于不同的检查场景——从简单的值比较到超时检查和分组断言。
特别值得关注的是assertThrows方法,它可以检查代码是否正确处理了错误情况——除以零、传递null、超出限制。
@Test
void divisionByZeroShouldThrowException() {
Calculator calc = new Calculator();
ArithmeticException exception = assertThrows(
ArithmeticException.class,
() -> calc.divide(10, 0)
);
assertEquals("Cannot divide by zero", exception.getMessage());
}
参数化测试允许使用不同的参数集运行同一个测试方法。在JUnit 5中,使用@ParameterizedTest注解结合数据源——@ValueSource、@CsvSource、@MethodSource或@EnumSource。
这种方法显著减少了代码重复:不需要为不同的值编写十个相同的测试,只需编写一个参数化方法。根据Google Testing Blog的数据,参数化可以在不损失覆盖率的情况下将测试代码量减少40-60%。
@CsvSource注解向测试传递多行逗号分隔的数据。每行对应一次测试运行。JUnit会自动将字符串值转换为所需类型:int、long、String等。
@ParameterizedTest
@CsvSource({
"1, 1, 2",
"2, 3, 5",
"10, 20, 30",
"-1, 1, 0"
})
void additionWithMultipleInputs(int a, int b, int expected) {
assertEquals(expected, a + b);
}
当输入数据比简单数字或字符串更复杂时,使用@MethodSource。它引用一个返回参数Stream的静态方法——例如,Arguments的Stream或用于测试DAO层的User对象Stream。
在Android开发中,JUnit与AndroidX Test配合使用,后者提供了用于测试Activity、Content Provider和其他Android组件的扩展。JUnit负责在JVM上执行的单元测试(无需模拟器),而AndroidX Test负责在设备上执行的仪器化测试。
在Android项目中添加JUnit 5,只需在模块级别的build.gradle中添加org.junit.jupiter:junit-jupiter依赖。AGP Gradle插件通过标准Test Runner支持在JVM上运行JUnit 5测试。
// build.gradle.kts
android {
testOptions {
unitTests.isIncludeAndroidResources = true
}
}
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter:5.11.0")
testImplementation("androidx.test:core-ktx:1.6.1")
}
在实践中,JUnit最常用于测试ViewModel和仓库层——这些层不需要Android上下文或模拟器。这样的测试在毫秒内完成,可以运行数百次而不会浪费时间。
JUnit 5(Jupiter)不仅仅是一个新版本,而是一个完全重新设计的平台,分为三个模块:JUnit Platform(在JVM上运行测试)、JUnit Jupiter(编写测试的API)和JUnit Vintage(与JUnit 4的向后兼容性)。这种模块化允许连接不同的Test Engine——例如,用于Kotlin的Spek或TestNG。
| 特性 | JUnit 4 | JUnit 5 |
|---|---|---|
| 包 | org.junit | org.junit.jupiter |
| 基本注解 | @Test(来自junit.framework) | @Test(来自org.junit.jupiter.api) |
| Before/After | @Before、@After、@BeforeClass | @BeforeEach、@AfterEach、@BeforeAll |
| 参数化 | @RunWith(Parameterized.class) | @ParameterizedTest + @ValueSource |
| 扩展 | @Rule、@ClassRule | @ExtendWith,更灵活的API |
| 最低Java版本 | Java 5 | Java 8+ |
从JUnit 4迁移到JUnit 5不需要重写所有测试——只需添加JUnit Vintage Engine依赖,旧测试将继续运行。建议使用JUnit 5编写新测试,以便使用扩展、断言中的lambda表达式以及内置的参数化支持。
常见问题
JUnit — 是用于编写和运行测试的框架,而Mockito是用于创建模拟对象的库。它们一起使用:JUnit管理测试执行,Mockito替换被测类的依赖。
是的,JUnit 5与Android项目完全兼容。对于单元测试,只需在build.gradle中添加junit-jupiter依赖即可。仪器化测试仍然通过AndroidX Test Runner运行。
最少只需要方法前的一个@Test注解。推荐使用@BeforeEach和@AfterEach进行初始化和清理,但它们不是必需的。
Test Runner — 是JUnit的一个组件,它扫描classpath,查找带有@Test注解的方法,创建测试类实例并运行测试。在JUnit 5中,这个角色由JUnit Platform配合连接的Test Engine执行。
使用assertThrows(Class, Executable) — 它接受预期的异常类型和方法调用的lambda表达式。JUnit检查异常是否确实被抛出,并返回该异常以供进一步检查。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。