3-3.Web环境模拟测试
约 1474 字大约 5 分钟
2025-06-24
在对表现层功能进行测试时,需要具备两个关键要素:一是运行测试程序时必须启动 Web 环境,二是测试程序能够发送 Web 请求。因此,表现层接口的测试可以分解为以下两个步骤:
- 在测试类中启动 Web 环境。
- 在测试类中发送 Web 请求。
1. 测试类中启动 Web 环境
每个 Spring Boot 测试类通常会使用 @SpringBootTest
注解。该注解的 webEnvironment
属性允许配置在测试用例中启动 Web 环境。
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class WebTest {
}
webEnvironment
属性支持以下四种设置值:
- MOCK:根据当前环境设置决定是否启动 Web 环境。例如,如果代码中使用了 Servlet API,则启动 Web 环境。
- DEFINED_PORT:使用自定义的端口作为 Web 服务器端口。
- RANDOM_PORT:使用随机端口作为 Web 服务器端口。建议使用此选项,以避免因硬编码端口而导致线上环境端口冲突。
- NONE:不启动 Web 环境。
使用 RANDOM_PORT
有助于及早发现代码中硬编码端口的问题,避免潜在的线上故障。
2. 测试类中发送请求
Java API 本身提供了发送 Web 请求的功能,Spring Boot 为了简化开发,对其进行了封装。具体步骤如下:
步骤 1:开启 Web 虚拟调用功能
通过 @AutoConfigureMockMvc
注解开启 Web 虚拟调用功能。
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
// 开启虚拟 MVC 调用
@AutoConfigureMockMvc
public class WebTest {
}
步骤 2:初始化 MockMvc
对象
通过自动装配初始化 MockMvc
对象,用于发起虚拟调用。
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
// 开启虚拟 MVC 调用
@AutoConfigureMockMvc
public class WebTest {
@Autowired
private MockMvc mvc;
@Test
void testWeb() throws Exception {
}
}
**步骤 3:创建虚拟请求对象 **
创建虚拟请求对象 MockHttpServletRequestBuilder
,封装请求路径,并使用 MockMvc
对象发送请求。
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
// 开启虚拟 MVC 调用
@AutoConfigureMockMvc
public class WebTest {
@Autowired
private MockMvc mvc;
@Test
void testWeb() throws Exception {
// 创建虚拟请求,当前访问 /books
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
// 执行对应的请求
mvc.perform(builder);
}
}
执行测试程序后,即可成功发送 /books
请求。注意,访问路径只需指定具体路径,无需包含服务器 IP 地址和端口,因为使用的是虚拟 Web 环境。
问题
虽然现在已经成功发送了请求,但还没有实现真正的测试效果。为了验证测试结果是否通过,需要将预期值与实际值进行比较。那么,虚拟请求中可以对哪些请求结果进行比对呢?
3. Web 环境请求结果比对
发送请求后,可以获取响应对象,然后对响应对象中的内容进行比对。常见的比对内容包括:
- 响应状态匹配
- 响应体匹配(非 JSON 数据格式)
- 响应体匹配(JSON 数据格式)
- 响应头信息匹配
下面分别介绍这些比对方式。
响应状态匹配
@Test
void testStatus(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
ResultActions action = mvc.perform(builder);
// 设定预期值与真实值进行比较,成功测试通过,失败测试失败
// 定义本次调用的预期值
StatusResultMatchers status = MockMvcResultMatchers.status();
// 预计本次调用是成功的:状态 200
ResultMatcher ok = status.isOk();
// 添加预计值到本次调用过程中进行匹配
action.andExpect(ok);
}
这段代码验证响应状态码是否为 200 (OK),使用 MockMvcResultMatchers.status().isOk()
来匹配预期的成功状态。
响应体匹配(非 JSON 数据格式)
@Test
void testBody(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
ResultActions action = mvc.perform(builder);
// 设定预期值与真实值进行比较,成功测试通过,失败测试失败
// 定义本次调用的预期值
ContentResultMatchers content = MockMvcResultMatchers.content();
ResultMatcher result = content.string("springboot2");
// 添加预计值到本次调用过程中进行匹配
action.andExpect(result);
}
这里,代码验证响应体是否为字符串 "springboot2", 使用 MockMvcResultMatchers.content().string("springboot2")
来匹配响应内容。
响应体匹配(JSON 数据格式,开发中的主流使用方式)
@Test
void testJson(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
ResultActions action = mvc.perform(builder);
// 设定预期值与真实值进行比较,成功测试通过,失败测试失败
// 定义本次调用的预期值
ContentResultMatchers content = MockMvcResultMatchers.content();
ResultMatcher result = content.json("{\"id\":1,\"name\":\"springboot2\",\"type\":\"springboot\"}");
// 添加预计值到本次调用过程中进行匹配
action.andExpect(result);
}
这段代码用于验证响应体是否为指定的 JSON 字符串。MockMvcResultMatchers.content().json("{\"id\":1,\"name\":\"springboot2\",\"type\":\"springboot\"}")
用于精确匹配 JSON 格式的响应内容。在实际开发中,这种方式非常常用,可以确保接口返回的数据结构和内容符合预期。
响应头信息匹配
@Test
void testContentType(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
ResultActions action = mvc.perform(builder);
// 设定预期值与真实值进行比较,成功测试通过,失败测试失败
// 定义本次调用的预期值
HeaderResultMatchers header = MockMvcResultMatchers.header();
ResultMatcher contentType = header.string("Content-Type", "application/json");
// 添加预计值到本次调用过程中进行匹配
action.andExpect(contentType);
}
该代码验证响应头中的 Content-Type
是否为 application/json
。MockMvcResultMatchers.header().string("Content-Type", "application/json")
用于匹配指定的响应头信息。
同时匹配响应状态、响应头和响应体信息
通过组合上述匹配方式,可以对响应结果进行全面的校验。以下示例展示了如何同时匹配响应状态、响应头和响应体信息:
@Test
void testGetById(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
ResultActions action = mvc.perform(builder);
StatusResultMatchers status = MockMvcResultMatchers.status();
ResultMatcher ok = status.isOk();
action.andExpect(ok);
HeaderResultMatchers header = MockMvcResultMatchers.header();
ResultMatcher contentType = header.string("Content-Type", "application/json");
action.andExpect(contentType);
ContentResultMatchers content = MockMvcResultMatchers.content();
ResultMatcher result = content.json("{\"id\":1,\"name\":\"springboot\",\"type\":\"springboot\"}");
action.andExpect(result);
}
这段代码首先验证响应状态是否为 200 (OK),然后验证 Content-Type
是否为 application/json
,最后验证响应体是否为指定的 JSON 字符串。