Appearance
🧪 单元测试
单元测试概述
单元测试是测试代码的最小单元,确保代码的正确性。
JUnit 5(Jupiter)
基本使用
java
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
class CalculatorTest {
Calculator calc;
@BeforeEach void setUp() { calc = new Calculator(); }
@Test void add() { assertEquals(8, calc.add(5, 3)); }
@Test void subtract() { assertEquals(2, calc.subtract(5, 3)); }
}常用注解速览
java
@BeforeAll // 类级别前置(需static)
@AfterAll // 类级别后置(需static)
@BeforeEach // 方法级前置
@AfterEach // 方法级后置
@DisplayName("描述性名称")
@Disabled("临时禁用原因")参数化测试
java
@ParameterizedTest
@CsvSource({"2,3,5", "-1,1,0"})
void add_param(int a, int b, int sum) {
assertEquals(sum, new Calculator().add(a, b));
}断言与异常
java
assertAll(
() -> assertEquals(9, Math.addExact(4,5)),
() -> assertThrows(ArithmeticException.class, () -> Math.addExact(Integer.MAX_VALUE, 1))
);超时与条件执行
java
@Test
void completesFast() {
assertTimeout(Duration.ofMillis(200), () -> service.call());
}
@EnabledOnOs(OS.WINDOWS)
@Test void onlyOnWindows() {}Mockito
Mock 对象
java
import static org.mockito.Mockito.*;
// 创建 Mock 对象
UserService userService = mock(UserService.class);
// 设置行为
when(userService.getUser(1)).thenReturn(new User("张三"));
// 验证调用
verify(userService).getUser(1);行为校验与参数匹配
java
when(repo.findById(anyLong())).thenReturn(Optional.of(new User("张三")));
verify(repo, times(1)).findById(argThat(id -> id > 0));部分 Mock 与 Spy
java
List<String> list = new ArrayList<>();
List<String> spy = spy(list);
doReturn(100).when(spy).size();Spring 测试(片段)
java
@SpringBootTest
@AutoConfigureMockMvc
class ApiTest {
@Autowired MockMvc mvc;
@Test void get_ok() throws Exception {
mvc.perform(get("/api/users/1")).andExpect(status().isOk());
}
}测试金字塔与策略(图示)
端到端(E2E)
────────────────── 稀疏、覆盖关键路径
服务/集成
────────────────── 组件交互、契约与持久化
单元
────────────────── 数量最多、快速稳定建议:单元测试覆盖核心逻辑;集成测试验证外部交互;E2E 只覆盖最关键用户旅程。
持续集成(CI)与覆盖率
- 在 CI 中使用
mvn -T 1C -DskipITs test或gradle test并启用缓存。 - 覆盖率工具:JaCoCo;门禁阈值示例 80%(按模块设置不同阈值更现实)。
xml
<!-- Maven Jacoco 片段 -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.11</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>verify</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>快速失败与可测试性设计
- 依赖倒置(端口/适配器)与注入,使得组件可 Mock;
- 纯函数与小函数有利于高覆盖率与定位;
- 避免静态全局状态与时间/线程随机性,抽象出时钟与执行器。
下一步
掌握了单元测试后,可以继续学习:
💡 提示:单元测试是保证代码质量的重要手段,应该养成编写单元测试的习惯
