如何编写Controller的测试代码?

张开发
2026/4/18 3:35:50 15 分钟阅读

分享文章

如何编写Controller的测试代码?
在日常工作中看过很多开发人员不写代码测试大部分理由是“太忙“或者”没必要”更严重的是很多开发人员甚至不知道如何写测试代码本文我们总结了一位腾讯后端对 Controller层代码优秀的测试经验希望对你有帮助。在 如何编写优雅的 Controller代码 这篇文章中我们分析了 Controller层的 6个主要职责本文将分析如何对 Controller层进行 360度无死角测试首先回顾下 Controller层的6个主要职责接收 HTTP(s)请求解析请求参数校验请求参数调用业务方法组织返回数据统一异常处理理解了 Controller的职责我们才能更有目的对它进行测试如下代码包含了 Controller的 6个主要职责我们该如何编写测试代码RestController public class UserController { private final UserService userService; public UserController(UserService userService) { this.userService userService; } PostMapping(/user/register) public String getGradeById(Validated RequestBody User user) { // 调用注册的业务方法 String userId userService.register(user); return userId; } } public class User { NotBlank(message Nickname is required.) private String nickname; private Integer age; // getters and setters and constructors }为什么要测试作为一名程序员代码质量是我们必须守住的底线对于自己编写的代码一定需要进过测试而不是把测试工作好不负责的全部交给测试人员这样才能保证代码的可靠性。对于开发人员保证代码质量最有效的方式是测试最常见的测试方式有 2种单元测试Unit Testing和 集成测试Integration Testing。单元测试的目的是验证单个功能单元的行为是否正确这里的功能单元通常是一个类或方法单元测试最大的优点是可以在不依赖于其他部分的前提下测试某个功能单元。集成测试的目的是验证多个功能单元之间的交互是否正确可能涉及数据库、文件系统、网络等外部系统如果需要排除外部系统的干扰可以对他们进行 mock。如何测试对于 Controller层的代码测试因为单元测试的局限性所以我们需要采用集成测试本文将使用SpringBootTest和Mockito两个主流的测试框架进行分析下面先说明几个重要的组件WebMvcTestWebMvcTest是 Spring Boot提供的一个注解用于测试 SpringMVC的 Controller它能够启动一个最小化的 Spring应用上下文包含仅与 Web层相关的 bean从而快速且高效地测试 Controller的功能。WebMvcTest主要功能如下加载 Web层相关的 Bean: WebMvcTest只加载与 Web层相关的 bean例如 Controller、过滤器、拦截器等这使得测试环境更加轻量级。自动配置 MockMvc: WebMvcTest 自动配置 MockMvc使得测试控制器的 HTTP请求处理变得简单。支持 MockBean注解: 可以使用 MockBean注解来模拟服务层的 bean以隔离控制器测试不依赖实际的服务层实现。MockMvcMockMvc是 Spring框架中用于测试 Spring MVC控制器的主要工具它允许开发者在不启动整个 HTTP服务器的情况下测试控制器的请求处理方法。MockMvc 提供了一种方式来模拟 HTTP请求并验证响应确保控制器行为符合预期。MockMvc的主要功能如下模拟 HTTP 请求: MockMvc 可以模拟各种 HTTP 请求如 GET、POST、PUT、DELETE 等。验证响应: MockMvc 提供了一组丰富的断言来验证响应的状态码、内容类型、内容、头信息等。测试控制器逻辑: 通过模拟请求并验证响应可以测试控制器的业务逻辑、路径变量、请求参数、请求体等。集成测试: 可以与 Spring 的其他测试工具结合进行更复杂的集成测试。MockitoMockito 是一个流行的 Java测试框架用于创建和配置 mock 对象。它主要用于单元测试中通过模拟依赖对象的行为使得测试目标对象能够独立于其依赖项进行测试。Mockito主要功能如下创建 Mock 对象: 使用 Mock注解或 Mockito.mock()方法创建 mock对象。定义行为: 使用 when 和 thenReturn 等方法定义 mock 对象的方法行为。验证行为: 使用 verify 方法验证 mock 对象的方法是否被调用以及调用的次数和参数。参数匹配: 使用 any、eq 等参数匹配器来验证方法调用时传入的参数。模拟异常: 使用 thenThrow 方法模拟方法抛出异常的行为。ObjectMapper它是 Jackson库中的核心类之一用于将 Java 对象转换为 JSON 字符串或者将 JSON 字符串转换为 Java 对象。在 Spring 应用中ObjectMapper 被广泛用于处理 JSON 数据。ObjectMapper的主要功能如下序列化Serialization: 将 Java 对象转换为 JSON 字符串。反序列化Deserialization: 将 JSON 字符串转换为 Java 对象。读取和写入 JSON 文件或流: 直接从文件、输入流读取 JSON 数据或将 JSON 数据写入文件、输出流。所以一个包含上述所有组件的完整代码示例如下WebMvcTest(controllers RegisterRestController.class) class RegisterRestControllerTest { Autowired private MockMvc mockMvc; Autowired private ObjectMapper objectMapper; MockBean private XXX xxx; // 用户业务的bean Test void test() throws Exception { mockMvc.perform(...); } }介绍了上面几个集成测试的组件之后接下来我们就可以详解的分析他们是如何应用到 Controller的集成测试中。测试详解我们按照 Controller的 6个主要职责来分别讲解他们是如何进行测试的。测试接收 HTTP(s)请求Controller是接收请求的入口因此对于 Controller接收 HTTP(s)请求验证 Controller是否侦听某个 HTTP 请求非常简单我们只需调用MockMvc.perform()方法返回 200代表成功返回非200就代表异常示例代码如下Test void whenReceiveHttpRqe_thenReturns200() throws Exception { mockMvc.perform(post(/user/register) .contentType(application/json)) .andExpect(status().isOk()); }测试解析请求参数Controller是通过PathVariable、RequestBody、RequestParam3种方式来接收参数的因此下面的示例代码分别模拟这 3种方式是如何进行参数传递的。Autowired private ObjectMapper objectMapper; Test void whenValidInput_thenReturns200() throws Exception { User user new User(zhangsan, 21); mockMvc.perform(post(/user/register/{id}, 1111) // 模拟通过 PathVariable传递参数 .contentType(application/json) .param(name, 张三) // 模拟通过 RequestParam传递参数 .content(objectMapper.writeValueAsString(user))) // 模拟通过 RequestBody传递参数 .andExpect(status().isOk()); }通过上面的方式我们模拟了一个正常的 HTTP请求并且使用 3种方式进行参数传递。测试校验请求参数测试参数的校验主要是为了检查代码逻辑有没有对参数进行有效的验证比如必填字段判空字符串最大长度限制数字最大值和最小值校验手机号或邮箱格式校验等。参数校验测试一般分正常测试和按预期失败的异常测试特别需要边界的测试。如下示例给出了测试通过和符合预期并返回 400的 BadRequestpublic class User { NotBlank(message Nickname is required.) private String nickname; private Integer age; // getters and setters and constructors } Test void whenNicknameNull_thenReturns400() throws Exception { User user new User(null, 21); // 当 nickname为空时会抛出 400的 BadRequest异常 mockMvc.perform(post(/user/register) .content(objectMapper.writeValueAsString(user))) .andExpect(status().isOk()); } Test void testReturns200() throws Exception { User user new User(zhangsan, 21); mockMvc.perform(post(/user/register) .content(objectMapper.writeValueAsString(user))) .andExpect(status().isBadRequest()); }测试调用业务方法对于业务方法调用的测试通常我们会使用Mockito.mock来模拟业务方法的返回值而业务方式的真实运行逻辑会在 Server的单元测试中完成如下示例代码// 调用注册的业务方法 String userId userService.register(user); Test void whenLogic_thenReturnsExceptData() throws Exception { User user new User(null, 21); // mock userService.register(user)返回值为 userId Mockito.when(userService.register(user)).thenReturn(userId); mockMvc.perform(post(/user/register) .content(objectMapper.writeValueAsString(user))) .andExpect(status().isOk()) .andExpect(jsonPath($.data, is(userId))); }测试组织返回数据在业务代码执行完之后我们需要对 HTTP对应相应因此可以使用 andReturn()方法将 HTTP交互的结果存储在 MvcResult类型的变量中然后从响应正文中读取 JSON字符串并使用 isEqualToIgnoringWhitespace()将其与预期字符串进行比较如下示例// 调用注册的业务方法 String userId userService.register(user); Test void whenLogic_thenReturnsExceptData() throws Exception { User user new User(zhangsan, 21); // mock userService.register(user)返回值为 userId Mockito.when(userService.register(user)).thenReturn(userId); MvcResult mvcResult mockMvc.perform(post(/user/register) .content(objectMapper.writeValueAsString(user))) .andReturn(); //将结果返回 String expectedResponse userId; String actualResponseBody mvcResult.getResponse().getContentAsString(); assertThat(actualResponseBody).isEqualToIgnoringWhitespace( objectMapper.writeValueAsString(expectedResponse)); }测试统一异常处理测试异是指如果发生异常Controller应返回特定的 HTTP状态比如 200400500等等 默认情况下Spring 会处理其中的大多数情况。但是如果我们有一个自定义异常处理我们想要测试它。假设我们要返回一个结构化的 JSON 错误响应其中包含请求中每个无效字段的字段名称和错误消息。我们会创建一个这样的ControllerAdviceTest void whenNullValue_thenReturns400AndErrorResult() throws Exception { User user new User(zhangsan, 21); MvcResult mvcResult mockMvc.perform(post(/user/register) .contentType(application/json) .content(objectMapper.writeValueAsString(user))) .andExpect(status().isBadRequest()) .andReturn(); ErrorResult expectedErrorResponse new ErrorResult(Nickname, Nickname is required.); String actualResponse mvcResult.getResponse().getContentAsString(); String expectedResponseBody objectMapper.writeValueAsString(expectedErrorResponse); assertThat(actualResponse).isEqualToIgnoringWhitespace(expectedErrorResponse); }总结本文结合SpringBootTest和Mockito两个主流的测试框架对 Controller各个职责进行全面的测试并且给出了比较详解的示例代码通过本文的分析我们不仅可以学会对 Controller的测试同时我们还应该触类旁通将里面优秀的思维应用到其他层级代码的测试。代码质量是开发人员必须守住的底线所以在日常的开发中一定要秉着对自己负责的态度对自己的代码进行测试。对于开发人员使用最多的测试方式是单元测试和集成测试。最后下方这份完整的软件测试 视频教程已经整理上传完成需要的朋友们可以自行领取【保证100%免费】​​​软件测试面试文档我们学习必然是为了找到高薪的工作下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料并且有字节大佬给出了权威的解答刷完这一套面试资料相信大家都能找到满意的工作。

更多文章