点击上方“程序员蜗牛g”,选择“设为星标”
跟蜗牛哥一起,每天进步一点点
程序员蜗牛g
大厂程序员一枚 跟蜗牛一起 每天进步一点点
33篇原创内容
**
公众号
实战案例
public record User(Long id, String name, Integer age) {}@Servicepublic class UserService { public User queryUser(Long id) { return new User(id, "Pack_xg", 33) ; }
public List<User> queryUsers() { return List.of(new User(1L, "pack", 33)) ; }
public User save(User user) { System.err.println("创建用户...") ; return user ; }}// Controller接口@RestController@RequestMapping("/users")public class UserController { private final UserService userService ; public UserController(UserService userService) { this.userService = userService ; } @GetMapping("/{id}") public User queryUser(@PathVariable Long id) { return this.userService.queryUser(id); }
@GetMapping("") public List<User> queryUsers() { return this.userService.queryUsers() ; }
@PostMapping("") @ResponseStatus(code = HttpStatus.CREATED) public User save(@RequestBody User user) { return user ; }}
接下来,我们将通过8种方法对上面的接口进行不同场景下的测试。
使用 @WebMvcTest
测试用例:
@WebMvcTest(UserController.class)public class UserControllerTest1 { // 用于模拟 HTTP 请求。 @Resource private MockMvc mockMvc; // 被模拟的服务(真实的服务被忽略)。 @MockitoBean private UserService userService; @Test public void testUsers() throws Exception { // 1.设置模拟行为 when(userService.queryUsers()).thenReturn(List.of(new User(1L, "pack", 33))) ; // 2.模拟 HTTP GET 请求到 /users 并验证响应 mockMvc.perform(get("/users")) .andExpect(status().isOk()) .andExpect(jsonPath("$[0].name").value("pack")); } @Test public void testUser() throws Exception { when(userService.queryUser(1L)) .thenThrow(new UserNotFoundException()); mockMvc.perform(get("/users/1")) .andExpect(status().isNotFound()); }}
何时使用 @WebMvcTest?
- 当你需要快速、专注地测试控制器逻辑时。
- 当你的重点是验证 HTTP 响应格式、状态码和异常处理机制时。
- 当你不需要启动完整的 Spring 上下文(如数据库、消息队列、完整安全配置)时。
@SpringBootTest + MockMvc 进行全面集成测试
与 @WebMvcTest 不同,此方法不会模拟服务或数据访问层(除非显式指定)。相反,它使用:
- 一个真实的(或内存中的)数据库(例如,测试时使用 H2)
- 实际的服务层(除非需要,否则不进行模拟)
- 完整的 Spring 上下文(与生产环境类似)
核心注解 & 类
- @SpringBootTest:启动完整的 Spring 应用程序上下文。
- @AutoConfigureMockMvc: 启用 MockMvc 以进行 HTTP 测试(无需启动真实服务器)。
测试用例:
@SpringBootTest@AutoConfigureMockMvcpublic class UserControllerTest2 { // 用于模拟 HTTP 请求。 @Resource private MockMvc mockMvc; // 被模拟的服务(真实的服务被忽略)。 @MockitoBean private UserService userService; @Test public void testSave() throws Exception { String jsonBody = """ {"id": 2, "name": "admin", "age": 33} """; mockMvc.perform(post("/users") .contentType(MediaType.APPLICATION_JSON) .content(jsonBody)) // 检查状态码是否201 .andExpect(status().isCreated()) // 检查返回值的name属性是否是admin .andExpect(jsonPath("$.name").value("admin")); }}
**
使用 WebTestClient 进行测试**
WebTestClient 是 MockMvc 和 TestRestTemplate 的现代、灵活替代方案,支持:
- ✅ 响应式应用(Spring WebFlux)
- ✅ 传统的阻塞式控制器(Spring MVC)
- ✅ 流畅的链式 API,便于进行请求/响应验证
测试用例:
// 使用下面2个注解都可以// @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)@WebFluxTest(UserController.class) public class UserControllerTest3 { @Resource private WebTestClient webTestClient; @MockitoBean private UserService userService; @Test public void testQueryUsers() throws Exception { when(userService.queryUsers()).thenReturn(List.of(new User(1L, "pack", 33))) ; webTestClient.get().uri("/users") .exchange() .expectStatus().isOk() .expectBody() .jsonPath("$[0].name").isEqualTo("pack"); }}
何时使用 WebTestClient?
- 若你的应用基于响应式(WebFlux)→ 首选方案。
- 若你希望使用统一工具测试 MVC 和 WebFlux 控制器。
- 若你偏好流畅、现代的断言风格,而非 MockMvc 的 DSL 风格。
2.4 使用 TestRestTemplate 进行测试
测试用例:
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)public class UserControllerTest4 { @Resource private TestRestTemplate restTemplate; @Test public void testQueryUsers() throws Exception { ResponseEntity<User[]> response = restTemplate.getForEntity("/users", User[].class); assertEquals(HttpStatus.OK, response.getStatusCode()); assertEquals("pack", response.getBody()[0].name()); }}
何时使用 TestRestTemplate?
- 测试 API 网关或代理
- 验证 HTTPS、CORS 或安全过滤器的行为
- 需要与外部服务交互的端到端测试
2.5 独立模式 MockMvc(无 Spring 上下文)
这种方式允许你在完全隔离的环境下测试单个控制器,无需:
- 加载 Spring 上下文
- 执行自动配置
- 触发过滤器、拦截器或 AOP 通知
取而代之的是:
- ✅ 手动创建控制器(并注入模拟的依赖)
- ✅ 使用 MockMvcBuilders.standaloneSetup()(无需 @SpringBootTest)
- ✅ 获得极快的测试执行速度(非常适合 TDD 快速反馈)
测试用例:
public class UserControllerTest5 { private MockMvc mockMvc; private UserService userService = mock(UserService.class); @BeforeEach public void setup() { // 1.手动创建Controller接口 UserController controller = new UserController(userService); // 2.不使用Spring构建MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); } @Test public void testQueryUsers() throws Exception { // 3.模拟测试行为 when(userService.queryUsers()).thenReturn(List.of(new User(1L, "pack", 33))); // 4.测试接口 mockMvc.perform(get("/users")) .andExpect(status().isOk()) .andExpect(jsonPath("$[0].name").value("pack1")); }}
何时使用 Standalone MockMvc?
- 需要完全隔离地测试控制器内部逻辑时
- 进行超高速单元测试,追求极致的 TDD 快速反馈
- 希望彻底避免 Spring 上下文启动开销的场景
REST Assured — 流畅 API 测试
目的
REST Assured 是一个用于测试 REST API 的 Java 领域特定语言(DSL),采用流畅的、行为驱动(BDD)风格编写测试。它:
- ✅ 使测试代码更具可读性(类似自然语言)
- ✅ 支持复杂验证(JSON Path、XML、Schema 等)
- ✅ 可与任何 HTTP 服务器(Spring Boot、Node.js 等)配合使用
测试用例:
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)public class UserControllerTest6 { // 注入随机端口 @LocalServerPort private int port; @Test public void testQueryUsers() { given().port(port) // 设置端口 // 当:调用 GET /users .when().get("/users") // 那么:验证响应 .then().statusCode(200) // HTTP 200 OK .body("[0].name", equalTo("pack")) // 检查 JSON .body("size()", greaterThan(1)); // 其他断言 }}
注意,你需要引入如下依赖:
<dependency> <groupId>io.rest-assured</groupId> <artifactId>rest-assured</artifactId> <scope>test</scope></dependency>
何时使用 REST Assured?
- 测试第三方或外部 API
- 编写易于理解、可读性高的集成测试
- 需要验证复杂的 JSON/XML 响应结构时
测试 Controller Advice 与异常处理
目的
验证以下关键环节:
- ✅ 全局异常处理器(@ControllerAdvice)能否正确捕获并处理异常
- ✅ 自定义错误响应(JSON/XML 格式)是否符合 API 规范
- ✅ 返回的 HTTP 状态码是否与错误类型匹配(如 404、400、500 等)
首先,准备@ControllerAdvice全局异常处理
@RestControllerAdvicepublic class UserControllerAdvice { @ExceptionHandler(UserNotFoundException.class) public ResponseEntity<?> error(UserNotFoundException e) { return ResponseEntity.status(404).body(Map.of("code", -1, "error", e.getMessage())) ; }}public class UserNotFoundException extends RuntimeException { public UserNotFoundException() { super(); } public UserNotFoundException(String message) { super(message); } public UserNotFoundException(String message, Throwable cause) { super(message, cause); }}
测试用例:
@WebMvcTest(UserController.class)public class UserControllerTest7 { // 用于模拟 HTTP 请求。 @Resource private MockMvc mockMvc; // 被模拟的服务 @MockitoBean private UserService userService; @Test public void testUser() throws Exception { when(userService.queryUser(1L)).thenThrow(new UserNotFoundException("用户不存在1")); mockMvc.perform(get("/users/1")) .andExpect(status().isNotFound()) .andExpect(jsonPath("$.error").value("用户不存在")); }}
最佳实践
- ✔ 结合测试模拟与真实异常场景:既测试手动抛异常的路径,也测试由框架触发的异常(如参数校验失败)
- ✔ 验证错误响应结构:确保返回的错误 JSON/XML 字段(如 code, message, timestamp)符合 API 文档规范
- ✔ 在测试中包含错误日志输出:使用 .andDo(print()) 打印请求/响应详情,便于调试失败用例
使用 Mockito 进行纯单元测试
目的
该方法完全绕过 HTTP 协议和 Spring 框架,仅专注于测试 Java 方法调用。适用于:
- ✅ 隔离控制器逻辑(例如,调用服务前的请求参数处理与业务判断)
- ✅ 极致快速的单元测试(无 Spring 上下文启动、无 HTTP 开销)
- ✅ 验证与依赖组件的交互(如是否正确调用 UserService 及参数传递)
测试用例:
public class UserControllerTest8 { private UserService userService = mock(UserService.class); private UserController userController = new UserController(userService); @Test public void testQueryUsers() throws Exception { when(userService.queryUsers()).thenReturn(List.of(new User(1L, "pack", 33)));
List<User> users = userController.queryUsers() ; assertEquals("pack", users.get(0).name()); verify(userService).queryUsers() ; }}
何时使用纯 Mockito 测试?
- 测试控制器中的辅助方法或私有逻辑
- 验证控制器内复杂的条件判断或数据处理流程
如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。
关注公众号:woniuxgg,在公众号中回复:笔记 就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!