测试金字塔
大量快速单元测试、适量集成测试、少量端到端。优先测纯逻辑与边界,集成测跨层协作,E2E 只覆盖关键链路。
用 方法名_场景_预期(如 createOrder_insufficientStock_throws)命名测试,失败即知问题。
JUnit 5 基础
JUnit 5 用 @Test、@BeforeEach、assertThrows 等。断言用 Assertions,可读性强。
class CalculatorTest {
private Calculator calc;
@BeforeEach void setUp() { calc = new Calculator(); }
@Test
void add_positiveNumbers_returnsSum() {
assertEquals(3, calc.add(1, 2));
}
@Test
void divide_byZero_throws() {
assertThrows(ArithmeticException.class, () -> calc.divide(1, 0));
}
}Mockito 模拟
Mockito 创建虚假依赖,定义其返回值并验证交互,让单测只聚焦被测类。配合 @ExtendWith(MockitoExtension.class)。
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock InventoryRepository inventory;
@InjectMocks OrderService service;
@Test
void create_decreasesStock() {
when(inventory.decrease(anyLong(), anyInt())).thenReturn(true);
service.create(new CreateOrderCmd(1L, 2, "a@b.com"));
verify(inventory).decrease(1L, 2); // 验证调用
}
}内部纯逻辑直接测;只对数据库、第三方、时钟等外部依赖 mock,避免测试失真。
Spring 切片测试
用 @WebMvcTest(仅 Web 层)、@DataJpaTest(仅 JPA)、@SpringBootTest(全栈)按需加载上下文,加快测试。
@WebMvcTest(OrderController.class)
class OrderControllerTest {
@Autowired MockMvc mvc;
@MockBean OrderService service;
// 只加载 Web 层,service 被 mock
}MockMvc 测接口
MockMvc 在容器外模拟 HTTP 请求,断言状态码与响应体,覆盖 Controller 全链路(校验、异常处理)。
@Test
void get_existing_returns200() throws Exception {
when(service.findById(1L)).thenReturn(new OrderDTO(1L));
mvc.perform(get("/api/orders/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1));
}Testcontainers
Testcontainers 在测试中拉起真实数据库/中间件容器,兼顾真实性与速度,适合集成测试关键数据路径。
@Container
static PostgreSQLContainer<?> db =
new PostgreSQLContainer<>("postgres:16-alpine");
@Test
void save_and_read_roundtrip() {
// 用真实 pg 连接测试 JPA 映射与查询
}Testcontainers 依赖本地或 CI 的 Docker 环境。纯单测应不依赖它,分层隔离。
覆盖率
Jacoco 统计行/分支覆盖。目标是关键路径高覆盖,而非盲目追 100%。配合 CI 门禁防止劣化。
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
</plugin>常见陷阱与 FAQ
用 @BeforeEach 重建被测对象与状态,保证每个测试独立、可单独运行。
只对真正需要完整上下文的用例用;其余用 @WebMvcTest/@DataJpaTest 等切片测试,大幅提速。
覆盖率只说明"执行过",不说明"断言正确"。关注关键分支与业务断言质量。