SpringBootMockMvc测试控制器

wen java案例 1

本文目录导读:

SpringBootMockMvc测试控制器

  1. 基础环境配置
  2. 测试方式一:@WebMvcTest(推荐)
  3. 测试方式二:@SpringBootTest
  4. 测试工具方法封装
  5. 常用断言技巧
  6. 模拟安全认证
  7. 文件上传测试
  8. 最佳实践建议

我来详细介绍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测试控制器的常见场景,你可以根据实际需求选择合适的测试方式。

抱歉,评论功能暂时关闭!