1. 核心依赖 (Maven)
XML
<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:
java
@Service
public class UserService {
public String getUserName(Long id) {
return "user-" + id;
}
}
测试类:
java
@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:
java
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping("/{id}")
public String getUser(@PathVariable Long id) {
return "user-" + id;
}
}
测试类:
java
@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,避免依赖真实数据库。
java
@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,默认回滚事务。
java
@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)
java
import static org.assertj.core.api.Assertions.*;
assertThat(user.getName()).startsWith("user");
assertThat(list).hasSize(3).contains("a", "b");
8. 测试生命周期
java
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class LifecycleTest {
@BeforeAll
void initAll() { /* 无需 static */ }
}
9. 常用 Mockito 操作
java
// 验证调用次数
verify(userService, times(1)).getUserName(anyLong());
// 抛出异常
when(userService.getUserName(1L)).thenThrow(new RuntimeException("error"));
// 参数匹配
when(userService.getUserName(eq(1L))).thenReturn("user-1");
10. 测试覆盖率 (Jacoco)
java
<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()。