目录
1. HTTP 服务模拟 (MockWebServer)
2. 数据库容器化测试 (Testcontainers)
Spring框架进行接口测试包含对接口的功能测试,数据访问,性能测试等。功能测试,验证接口是否按照需求工作,比如正确的输入输出、业务逻辑处理。还有异常处理测试,比如当传入无效参数时,接口是否能正确返回错误信息。参数验证也是关键,比如使用@Validated注解时的验证逻辑是否生效。
数据访问测试,比如使用Spring Data JPA或MyBatis时,数据库操作是否正确,事务管理是否有效。权限控制测试,比如Spring Security的权限验证,不同角色用户访问接口的结果是否符合预期。
性能测试,比如接口的响应时间、并发处理能力。安全测试,比如SQL注入、XSS攻击的防护。依赖服务测试,比如模拟外部服务(如RESTful API、数据库)的行为,确保接口在依赖服务不可用时的处理。
Spring特有的测试工具,比如@WebMvcTest用于控制器层的测试,@DataJpaTest用于数据层的测试,以及如何利用MockBean来模拟Bean的行为。此外,异常处理测试中,可以提到使用@ControllerAdvice和@ExceptionHandler来统一处理异常,并测试这些处理是否正确。
一、基础 REST API 测试场景
1. GET 请求验证
@SpringBootTest
@AutoConfigureMockMvc
class UserControllerTest {
@Autowired MockMvc mockMvc;
@Test
void getUsers_shouldReturn200() throws Exception {
mockMvc.perform(get("/api/users"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].name").value("Alice"));
}
@Test
void getUserById_shouldReturn404() throws Exception {
mockMvc.perform(get("/api/users/999")) // 不存在的ID
.andExpect(status().isNotFound());
}
}
2. POST 请求验证
@Test
void createUser_shouldReturn201() throws Exception {
String jsonBody = """
{ "name": "Bob", "email": "bob@example.com" }
""";
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(jsonBody))
.andExpect(status().isCreated())
.andExpect(header().exists("Location"));
}
二、复杂业务场景
1. 参数校验失败测试
@Test
void createUser_invalidEmail_shouldReturn400() throws Exception {
String invalidJson = "{ \"name\": \"\", \"email\": \"invalid\" }";
mockMvc.perform(post("/api/users")
.content(invalidJson)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.errors").isArray());
}
2. 分页查询测试
@Test
void getUsers_withPagination() throws Exception {
mockMvc.perform(get("/api/users")
.param("page", "2")
.param("size", "10"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.content.length()").value(10));
}
三、安全相关场景
1. 认证失败测试
@Test
void getSecretData_withoutAuth_shouldReturn401() throws Exception {
mockMvc.perform(get("/api/secret"))
.andExpect(status().isUnauthorized());
}
2. 角色权限验证
@Test
@WithMockUser(roles = "USER")
void deleteUser_asUser_shouldReturn403() throws Exception {
mockMvc.perform(delete("/api/users/1"))
.andExpect(status().isForbidden());
}
@Test
@WithMockUser(roles = "ADMIN")
void deleteUser_asAdmin_shouldSucceed() throws Exception {
mockMvc.perform(delete("/api/users/1"))
.andExpect(status().isNoContent());
}
四、数据持久化场景
1. 数据库集成测试
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
class UserRepositoryTest {
@Autowired TestEntityManager entityManager;
@Autowired UserRepository repository;
@Test
void findByEmail_shouldReturnUser() {
User user = new User("test@example.com");
entityManager.persist(user);
Optional<User> found = repository.findByEmail("test@example.com");
assertThat(found).isPresent();
}
}
2. 事务回滚测试
@SpringBootTest
@Transactional
class UserServiceIntegrationTest {
@Autowired UserService service;
@Test
void createUser_shouldRollbackOnException() {
assertThrows(DataIntegrityViolationException.class, () -> {
service.createUser(new User(null)); // 无效数据
});
// 验证数据未持久化
assertThat(service.countUsers()).isEqualTo(0);
}
}
五、外部依赖处理场景
1. HTTP 服务模拟 (MockWebServer)
@SpringBootTest
@AutoConfigureMockMvc
@Testcontainers
class PaymentControllerTest {
@Container
static MockWebServer externalService = new MockWebServer();
@DynamicPropertySource
static void setProperties(DynamicPropertyRegistry registry) {
registry.add("payment.service.url",
() -> externalService.url("/").toString());
}
@Test
void processPayment() throws Exception {
// 设置模拟响应
externalService.enqueue(new MockResponse()
.setBody("{\"status\":\"SUCCESS\"}")
.addHeader("Content-Type", "application/json"));
mockMvc.perform(post("/api/payments"))
.andExpect(status().isOk());
}
}
2. 数据库容器化测试 (Testcontainers)
@Testcontainers
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
class UserRepositoryContainerTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15");
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}
@Test
void testDatabaseConnection() {
// 测试逻辑
}
}
六、异步接口测试
@Test
void asyncOperation_shouldComplete() throws Exception {
MvcResult result = mockMvc.perform(get("/api/async"))
.andExpect(request().asyncStarted())
.andReturn();
mockMvc.perform(asyncDispatch(result))
.andExpect(status().isOk())
.andExpect(content().string("Operation completed"));
}
七、文件上传/下载测试
1. 文件上传
@Test
void uploadFile_shouldSucceed() throws Exception {
MockMultipartFile file = new MockMultipartFile(
"file", "test.txt", "text/plain", "Hello World".getBytes()
);
mockMvc.perform(multipart("/api/files").file(file))
.andExpect(status().isOk());
}
2. 文件下载
@Test
void downloadFile_shouldReturnContent() throws Exception {
mockMvc.perform(get("/api/files/1"))
.andExpect(status().isOk())
.andExpect(header().string(
HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"report.pdf\""
))
.andExpect(content().contentType(MediaType.APPLICATION_PDF));
}
八、性能与压力测试
结合 Spring Boot Actuator 和 JMeter:
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class UserControllerLoadTest {
@LocalServerPort int port;
@Test
void stressTest() {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://localhost:" + port + "/api/users"))
.GET()
.build();
// 使用JMeter或自定义压力测试工具
loadTest(request, 1000); // 模拟1000并发请求
}
}
九、建议
测试分层策略
Controller 层:使用 MockMvc 模拟 HTTP 请求
Service 层:使用 @MockBean 模拟依赖
Repository 层:使用 @DataJpaTest + 嵌入式数据库
测试数据管理
使用 @Sql 注解初始化数据
测试后自动清理数据(@Transactional)
环境隔离
生产环境配置:application-prod.properties
测试环境配置:application-test.properties
使用 @ActiveProfiles("test") 激活配置