1. 核心依赖 (Maven)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>该 Starter 自动引入:
JUnit 5(Jupiter)
Mockito
AssertJ
Spring Test等
2. 基础注解
| 注解 | 作用 |
|---|---|
@SpringBootTest | 启动完整 Spring 上下文(集成测试) |
@Test | 标记测试方法 |
@BeforeEach/@AfterEach | 每个测试前/后执行 |
@BeforeAll/@AfterAll | 所有测试前/后执行(需静态方法) |
@DisplayName | 给测试起中文名 |
@Disabled | 禁用测试 |
3. 单元测试(Service 层)
被测 Service:
@Service public class UserService { public String getUserName(Long id) { return "user-" + id; } }测试类:
@SpringBootTest class UserServiceTest { @Autowired private UserService userService; @Test @DisplayName("根据ID获取用户名") void testGetUserName() { String name = userService.getUserName(1L); assertEquals("user-1", name); assertNotNull(name); } }4. Web 层测试(Controller)
使用@WebMvcTest只加载 Web 层,比@SpringBootTest轻量。
被测 Controller:
@RestController @RequestMapping("/api/users") public class UserController { @GetMapping("/{id}") public String getUser(@PathVariable Long id) { return "user-" + id; } }测试类:
@WebMvcTest(UserController.class) class UserControllerTest { @Autowired private MockMvc mockMvc; @Test void testGetUser() throws Exception { mockMvc.perform(get("/api/users/1")) .andExpect(status().isOk()) .andExpect(content().string("user-1")); } }5. Mock 依赖(隔离测试)
使用@MockBean模拟 Service,避免依赖真实数据库。
@SpringBootTest class UserControllerMockTest { @Autowired private MockMvc mockMvc; @MockBean private UserService userService; @Test void testGetUser() throws Exception { when(userService.getUserName(1L)).thenReturn("mock-user"); mockMvc.perform(get("/api/users/1")) .andExpect(status().isOk()) .andExpect(content().string("mock-user")); } }6. 数据层测试(Repository)
使用@DataJpaTest测试 JPA,默认回滚事务。
@DataJpaTest class UserRepositoryTest { @Autowired private UserRepository userRepository; @Test void testSaveAndFind() { User user = new User("test"); userRepository.save(user); Optional<User> found = userRepository.findByName("test"); assertTrue(found.isPresent()); } }7. 断言进阶(AssertJ)
import static org.assertj.core.api.Assertions.*; assertThat(user.getName()).startsWith("user"); assertThat(list).hasSize(3).contains("a", "b");8. 测试生命周期
@TestInstance(TestInstance.Lifecycle.PER_CLASS) class LifecycleTest { @BeforeAll void initAll() { /* 无需 static */ } }9. 常用 Mockito 操作
// 验证调用次数 verify(userService, times(1)).getUserName(anyLong()); // 抛出异常 when(userService.getUserName(1L)).thenThrow(new RuntimeException("error")); // 参数匹配 when(userService.getUserName(eq(1L))).thenReturn("user-1");10. 测试覆盖率 (Jacoco)
<plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.11</version> </plugin>执行mvn clean test后生成target/site/jacoco/index.html。
11. 最佳实践总结
| 类型 | 推荐注解 | 说明 |
|---|---|---|
| 单元测试(Service) | @SpringBootTest | 可配合@MockBean |
| Web 层测试 | @WebMvcTest | 只加载 Controller |
| Repository 测试 | @DataJpaTest | 轻量,自动回滚 |
| 集成测试 | @SpringBootTest | 加载完整上下文 |
| 测试命名 | should_xxx_when_xxx | 清晰表达意图 |
| 断言 | AssertJ | 更丰富流畅 |
12. 面试常见问题
@SpringBootTest和@WebMvcTest区别?@SpringBootTest:加载全部 Bean,适合集成测试。@WebMvcTest:只加载 Web 层,更快。
@MockBean和@Mock区别?@MockBean是 Spring 提供的,会将 Mock 对象注入 Spring 容器。@Mock是 Mockito 的,不管理容器。
如何测试异步方法?
使用@Async+CountDownLatch或CompletableFuture配合await()。