本文目录导读:

我来详细介绍Spring Boot中使用MockMvc进行控制器测试的方法。
基础环境配置
Maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
示例控制器
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.findById(id);
return ResponseEntity.ok(user);
}
@PostMapping
public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
User created = userService.save(user);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
return ResponseEntity.ok(userService.findAll());
}
}
测试方式一:@WebMvcTest(推荐)
基础测试类
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void testGetUser_Success() throws Exception {
// 准备测试数据
User mockUser = new User(1L, "张三", "zhangsan@example.com");
when(userService.findById(1L)).thenReturn(mockUser);
// 执行请求并验证
mockMvc.perform(get("/api/users/1")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("张三"))
.andExpect(jsonPath("$.email").value("zhangsan@example.com"));
}
}
完整测试示例
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Autowired
private ObjectMapper objectMapper;
// 测试获取用户信息
@Test
void testGetUser_Success() throws Exception {
User mockUser = new User(1L, "张三", "zhangsan@example.com");
when(userService.findById(1L)).thenReturn(mockUser);
mockMvc.perform(get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.name").value("张三"));
}
// 测试用户不存在
@Test
void testGetUser_NotFound() throws Exception {
when(userService.findById(99L)).thenThrow(new UserNotFoundException("用户不存在"));
mockMvc.perform(get("/api/users/99"))
.andExpect(status().isNotFound());
}
// 测试创建用户
@Test
void testCreateUser_Success() throws Exception {
User user = new User(null, "李四", "lisi@example.com");
User savedUser = new User(2L, "李四", "lisi@example.com");
when(userService.save(any(User.class))).thenReturn(savedUser);
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(user)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").value(2))
.andExpect(jsonPath("$.name").value("李四"));
}
// 测试参数验证失败
@Test
void testCreateUser_ValidationFailed() throws Exception {
User invalidUser = new User(null, "", "invalid-email");
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(invalidUser)))
.andExpect(status().isBadRequest());
}
}
测试方式二:@SpringBootTest
集成测试
@SpringBootTest
@AutoConfigureMockMvc
class UserControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private UserRepository userRepository;
@BeforeEach
void setUp() {
userRepository.deleteAll();
}
@Test
void testFullFlow() throws Exception {
// 创建用户
String userJson = "{\"name\":\"王五\",\"email\":\"wangwu@example.com\"}";
MvcResult result = mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(userJson))
.andExpect(status().isCreated())
.andReturn();
String responseBody = result.getResponse().getContentAsString();
User createdUser = objectMapper.readValue(responseBody, User.class);
// 查询用户
mockMvc.perform(get("/api/users/" + createdUser.getId()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("王五"));
}
}
测试工具方法封装
通用测试工具类
@Component
public class TestUtils {
public static ResultActions performGet(MockMvc mockMvc, String url) throws Exception {
return mockMvc.perform(get(url)
.accept(MediaType.APPLICATION_JSON));
}
public static ResultActions performPost(MockMvc mockMvc, String url, Object body) throws Exception {
ObjectMapper mapper = new ObjectMapper();
return mockMvc.perform(post(url)
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(body)));
}
public static <T> T parseResponse(MvcResult result, Class<T> clazz) throws Exception {
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(result.getResponse().getContentAsString(), clazz);
}
}
常用断言技巧
多种断言示例
class AdvancedAssertionsTest {
@Test
void testAdvancedAssertions() throws Exception {
// JSON路径断言
mockMvc.perform(get("/api/users"))
.andExpect(jsonPath("$", hasSize(3)))
.andExpect(jsonPath("$[0].name").value("张三"))
.andExpect(jsonPath("$[0].age", greaterThan(18)))
.andExpect(jsonPath("$[*].name", hasItem("李四")))
.andExpect(jsonPath("$[?(@.name == '王五')]").exists());
// 响应头断言
mockMvc.perform(get("/api/users"))
.andExpect(header().string("Content-Type", containsString("application/json")))
.andExpect(header().exists("X-Request-ID"));
// Cookie断言
mockMvc.perform(get("/api/users")
.cookie(new Cookie("session-id", "abc123")))
.andExpect(cookie().exists("session-id"));
// 请求参数断言
mockMvc.perform(get("/api/users")
.param("page", "1")
.param("size", "10"))
.andExpect(status().isOk());
}
}
模拟安全认证
带安全认证的测试
@WebMvcTest(UserController.class)
class SecurityTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
@WithMockUser(username = "admin", roles = {"ADMIN"})
void testAdminEndpoint() throws Exception {
mockMvc.perform(get("/api/admin/users"))
.andExpect(status().isOk());
}
@Test
@WithAnonymousUser
void testUnauthorized() throws Exception {
mockMvc.perform(get("/api/admin/users"))
.andExpect(status().isUnauthorized());
}
@Test
@WithMockUser(username = "user", roles = {"USER"})
void testForbidden() throws Exception {
mockMvc.perform(get("/api/admin/users"))
.andExpect(status().isForbidden());
}
}
文件上传测试
文件上传示例
class FileUploadTest {
@Test
void testFileUpload() throws Exception {
MockMultipartFile file = new MockMultipartFile(
"file",
"test.txt",
"text/plain",
"Hello, World!".getBytes()
);
mockMvc.perform(multipart("/api/upload")
.file(file)
.param("description", "测试文件"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.fileName").value("test.txt"));
}
}
最佳实践建议
测试结构组织
class UserControllerTest {
// 使用Nested组织测试
@Nested
class GetUserTests {
@Test
void shouldReturnUserWhenExists() {}
@Test
void shouldReturn404WhenNotExists() {}
}
@Nested
class CreateUserTests {
@Test
void shouldCreateUserWithValidData() {}
@Test
void shouldReturn400WithInvalidData() {}
}
}
这些示例涵盖了Spring Boot中使用MockMvc测试控制器的常见场景,你可以根据实际需求选择合适的测试方式。