文档站点
Skip to content

HTTP 测试 ​

Laravel 用 $this->get('/posts') + assertStatus;Spring Boot 用 MockMvc 模拟 HTTP 请求、断言状态码和响应体。MockMvc 不打真实端口,直接在内存里跑完整 MVC 链路 —— 又全又省事。

依赖 ​

spring-boot-starter-test 已包含 MockMvc 和 JUnit 5,只需引入:

xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

第一个 HTTP 测试 ​

java
package com.example.blog.post;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@SpringBootTest
@AutoConfigureMockMvc
class PostControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void 获取帖子列表返回200() throws Exception {
        mockMvc.perform(get("/api/posts"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.length()").value(2));      // JSON 数组长度
    }

    @Test
    void 查询不存在的帖子返回404() throws Exception {
        mockMvc.perform(get("/api/posts/99999"))
                .andExpect(status().isNotFound())
                .andExpect(jsonPath("$.code").value(404));
    }
}

对应 Laravel

LaravelMockMvc
$this->get('/posts')mockMvc.perform(get("/api/posts"))
->assertStatus(200).andExpect(status().isOk())
->assertJson(['title' => ...]).andExpect(jsonPath("$.title").value(...))
->assertJsonCount(2).andExpect(jsonPath("$.length()").value(2))

常用请求构造 ​

java
// GET + 查询参数
mockMvc.perform(get("/api/posts")
        .param("page", "1")
        .param("size", "10"));

// POST + JSON body
mockMvc.perform(post("/api/posts")
        .contentType(MediaType.APPLICATION_JSON)
        .content("{\"title\":\"测试\",\"content\":\"内容\"}"));

// 带请求头(如 token)
mockMvc.perform(get("/api/me")
        .header("Authorization", "Bearer test-token"));

// 路径参数
mockMvc.perform(get("/api/posts/{id}", 1));

常用断言 ​

java
// 状态码
.andExpect(status().isOk())            // 200
.andExpect(status().isCreated())       // 201
.andExpect(status().isNoContent())     // 204
.andExpect(status().isUnauthorized())  // 401
.andExpect(status().isForbidden())     // 403
.andExpect(status().isNotFound())      // 404
.andExpect(status().isBadRequest())    // 400

// JSON 结构
.andExpect(jsonPath("$.data.title").value("测试"))
.andExpect(jsonPath("$.data.status").isNumber())
.andExpect(jsonPath("$.data.list[0].id").value(1))
.andExpect(jsonPath("$.data").isEmpty())

// 响应头
.andExpect(header().string("Content-Type", containsString("application/json")))

TIP

jsonPath 是 MockMvc 最强大的断言工具,用 JSONPath 语法查 JSON 任意位置($.data[0].title 等)。配合 @RestControllerAdvice 的统一响应结构,断言 $.code / $.message 非常顺。

集成测试要启动 Spring 吗 ​

@SpringBootTest 会启动完整应用上下文。几个常用注解:

注解行为
@SpringBootTest启动完整上下文(真实注入所有 Bean)
@AutoConfigureMockMvc自动配置 MockMvc
@WebMvcTest(PostController.class)只加载 Web 层(快,Controller 单测用)
@DataJpaTest只测 JPA 层(见 数据库测试)
java
// 轻量级:只测 Controller,Service 用 Mock 替身
@WebMvcTest(PostController.class)
class PostControllerWebTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private PostService postService;

    @Test
    void 列表接口() throws Exception {
        when(postService.list()).thenReturn(List.of(...));
        mockMvc.perform(get("/api/posts")).andExpect(status().isOk());
    }
}

@WebMvcTest vs @SpringBootTest

  • @WebMvcTest:只装配 Web 层,快,适合 Controller 单测(依赖 Mock)
  • @SpringBootTest:全量装配,慢,适合链路验证(真实数据库) Laravel 的 Feature 测试是全栈的,最接近 @SpringBootTest。

完整请求-响应断言示例 ​

java
@Test
void 创建帖子成功() throws Exception {
    String body = """
            {"title":"Spring 测试","content":"正文内容"}
            """;
    mockMvc.perform(post("/api/posts")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(body))
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.code").value(0))
            .andExpect(jsonPath("$.data.title").value("Spring 测试"));
}

@Test
void 标题为空校验失败() throws Exception {
    String body = "{\"title\":\"\",\"content\":\"x\"}";
    mockMvc.perform(post("/api/posts")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(body))
            .andExpect(status().isBadRequest())
            .andExpect(jsonPath("$.code").value(400));
}

响应 JSON 复杂断言 ​

java
// 数组长度
jsonPath("$.data.items.length()").value(3)
// 是否存在某个字段
jsonPath("$.data.token").exists()
// 是否匹配正则
jsonPath("$.data.phone").value(matchesPattern("^1\\d{10}$"))

实践建议 ​

  1. Controller 测「请求→响应」契约:状态码、JSON 结构、校验错误 —— 别测业务逻辑
  2. 业务逻辑留给 Service 测试(模拟对象 Mockito)
  3. 用中文方法名描述行为
  4. 与数据库打交道的测试见 数据库测试

下一节:数据库测试。

面向 PHP / Laravel 开发者的 Spring Boot 中文文档