聊一聊Spring框架接口测试常见场景有哪些?
目录
一、基础 REST API 测试场景
1. GET 请求验证
2. POST 请求验证
二、复杂业务场景
1. 参数校验失败测试
2. 分页查询测试
三、安全相关场景
1. 认证失败测试
2. 角色权限验证
四、数据持久化场景
1. 数据库集成测试
2. 事务回滚测试
五、外部依赖处理场景
1. HTTP 服务模拟 (MockWebServer)
2. 数据库容器化测试 (Testcontainers)
六、异步接口测试
七、文件上传/下载测试
1. 文件上传
2. 文件下载
八、性能与压力测试
九、建议
测试分层策略
测试数据管理
环境隔离
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") 激活配置