8种让 Spring Boot 接口稳定性提升 200%的测试方法,

48 阅读4分钟

点击上方“程序员蜗牛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.classpublic 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实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!