MockMvcBuilders.webAppContextSetup(WebApplicationContext).build() 详解
一、代码段解析
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build()
这行代码是Spring测试框架中创建MockMvc实例的标准方式,其作用如下:
- MockMvcBuilders:工厂类,用于构建MockMvc实例
- webAppContextSetup():指定使用完整的Web应用上下文来构建MockMvc
- webApplicationContext:已加载的Spring Web应用上下文
- build():最终构建出MockMvc实例
二、MockMvc类的作用
MockMvc是Spring MVC测试框架的核心类,主要提供以下功能:
1. 模拟HTTP请求
- 支持所有HTTP方法(GET/POST/PUT/DELETE等)
- 可以模拟请求头、参数、cookie、session等
- 支持文件上传等复杂请求
2. 验证响应
- 响应状态码验证
- 响应头验证
- 响应内容验证(JSON/XML/HTML等)
- 重定向验证
3. 测试优势
- 不需要启动真实服务器
- 快速执行控制器测试
- 支持完整的Spring MVC流程
三、webAppContextSetup详解
1. 工作流程
mermaid
graph TD
A[加载WebApplicationContext] --> B[初始化DispatcherServlet]
B --> C[配置所有已注册的Filter]
C --> D[构建MockMvc实例]
graph TD
A[加载WebApplicationContext] --> B[初始化DispatcherServlet]
B --> C[配置所有已注册的Filter]
C --> D[构建MockMvc实例]
2. 包含的组件
- 所有
@Controller
和@RestController
bean - 所有配置的拦截器(Interceptors)
- 所有注册的过滤器(Filters)
- 消息转换器(Message Converters)
- 视图解析器(View Resolvers)
3. 与standaloneSetup对比
特性 | webAppContextSetup | standaloneSetup |
---|---|---|
上下文 | 完整Web应用上下文 | 仅指定控制器的模拟上下文 |
自动配置 | 包含所有自动配置的Bean | 需要手动配置依赖 |
过滤器/拦截器 | 自动包含 | 需要手动添加 |
使用场景 | 集成测试 | 单元测试 |
启动速度 | 较慢 | 较快 |
四、实际应用示例
1. 基础使用
java
@SpringBootTest
public class UserControllerTest {
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
@BeforeEach
void setup() {
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.alwaysDo(print()) // 总是打印请求响应信息
.build();
}
@Test
void getUser() throws Exception {
mockMvc.perform(get("/users/1")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("John"));
}
}
@SpringBootTest
public class UserControllerTest {
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
@BeforeEach
void setup() {
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.alwaysDo(print()) // 总是打印请求响应信息
.build();
}
@Test
void getUser() throws Exception {
mockMvc.perform(get("/users/1")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("John"));
}
}
2. 高级配置
java
@BeforeEach
void setup() {
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.addFilter(new CharacterEncodingFilter("UTF-8", true)) // 添加过滤器
.defaultRequest(get("/").contextPath("/api")) // 默认请求配置
.alwaysExpect(content().contentType(MediaType.APPLICATION_JSON)) // 全局断言
.build();
}
@BeforeEach
void setup() {
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.addFilter(new CharacterEncodingFilter("UTF-8", true)) // 添加过滤器
.defaultRequest(get("/").contextPath("/api")) // 默认请求配置
.alwaysExpect(content().contentType(MediaType.APPLICATION_JSON)) // 全局断言
.build();
}
五、核心API详解
1. 请求构建
java
mockMvc.perform(
post("/users") // HTTP方法+路径
.contentType(MediaType.APPLICATION_JSON) // 内容类型
.content("{\"name\":\"John\"}") // 请求体
.header("X-Auth", "token") // 自定义头
.cookie(new Cookie("test", "value")) // Cookie
.sessionAttr("user", testUser) // Session属性
);
mockMvc.perform(
post("/users") // HTTP方法+路径
.contentType(MediaType.APPLICATION_JSON) // 内容类型
.content("{\"name\":\"John\"}") // 请求体
.header("X-Auth", "token") // 自定义头
.cookie(new Cookie("test", "value")) // Cookie
.sessionAttr("user", testUser) // Session属性
);
2. 结果验证
java
.andExpect(status().isCreated()) // 状态码
.andExpect(header().string("Location", "/users/123")) // 响应头
.andExpect(jsonPath("$.id").value(123)) // JSON路径
.andExpect(content().string(containsString("success"))) // 内容包含
.andExpect(view().name("userView")) // 视图名称
.andExpect(model().attributeExists("user")) // 模型属性
.andExpect(status().isCreated()) // 状态码
.andExpect(header().string("Location", "/users/123")) // 响应头
.andExpect(jsonPath("$.id").value(123)) // JSON路径
.andExpect(content().string(containsString("success"))) // 内容包含
.andExpect(view().name("userView")) // 视图名称
.andExpect(model().attributeExists("user")) // 模型属性
3. 结果处理
java
.andDo(print()) // 打印请求响应详情
.andDo(document("user-create")) // Spring REST Docs文档生成
.andReturn(); // 获取完整MvcResult
.andDo(print()) // 打印请求响应详情
.andDo(document("user-create")) // Spring REST Docs文档生成
.andReturn(); // 获取完整MvcResult
六、实际应用场景
1. 测试REST API
java
@Test
void createUser() throws Exception {
String userJson = "{\"username\":\"test\",\"email\":\"test@example.com\"}";
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(userJson))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").exists());
}
@Test
void createUser() throws Exception {
String userJson = "{\"username\":\"test\",\"email\":\"test@example.com\"}";
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(userJson))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").exists());
}
2. 测试表单提交
java
@Test
void submitForm() throws Exception {
mockMvc.perform(post("/form")
.param("name", "John")
.param("age", "30"))
.andExpect(status().isOk())
.andExpect(model().attribute("result", "success"));
}
@Test
void submitForm() throws Exception {
mockMvc.perform(post("/form")
.param("name", "John")
.param("age", "30"))
.andExpect(status().isOk())
.andExpect(model().attribute("result", "success"));
}
3. 测试文件上传
java
@Test
void uploadFile() throws Exception {
MockMultipartFile file = new MockMultipartFile(
"file", "test.txt", "text/plain", "content".getBytes());
mockMvc.perform(multipart("/upload").file(file))
.andExpect(status().isOk());
}
@Test
void uploadFile() throws Exception {
MockMultipartFile file = new MockMultipartFile(
"file", "test.txt", "text/plain", "content".getBytes());
mockMvc.perform(multipart("/upload").file(file))
.andExpect(status().isOk());
}
七、最佳实践
- 测试隔离:每个测试方法应该独立,使用
@Transactional
和@Rollback
- 合理断言:优先验证业务逻辑,而非实现细节
- 日志输出:使用
andDo(print())
辅助调试 - 共享配置:抽象基础测试类减少重复代码
- 性能考虑:对于简单测试,考虑使用
standaloneSetup
通过这种方式创建的MockMvc实例能够完整模拟Web容器环境,同时保持测试的高效执行,是Spring MVC控制器测试的首选方式。