1. 概述
让我们深入探索Spring Boot测试的世界!本文将深度解析@SpringBootTest
和@WebMvcTest
注解,探讨它们的使用场景、工作原理以及如何协同测试Spring Boot应用。我们还将揭示MockMvc
的内部机制,以及它在集成测试中与这两个注解的交互方式。
2. @WebMvcTest和@SpringBootTest简介
@WebMvcTest
注解专门用于MVC(更具体说是控制器)层测试,可配置为测试特定控制器。它主要加载Web层组件,简化相关测试流程。
@SpringBootTest
注解通过加载完整的应用上下文(如带@Component
和@Service
注解的类、数据库连接等)创建测试环境。它会查找主类(带@SpringBootApplication
注解)并以此启动应用上下文。
这两个注解均在Spring Boot 1.4版本引入。
3. 项目搭建
本教程创建两个类:SortingController
和SortingService
。SortingController
接收包含整数列表的请求,调用包含业务逻辑的SortingService
对列表排序。
我们使用构造函数注入获取SortingService
依赖:
@RestController
public class SortingController {
private final SortingService sortingService;
public SortingController(SortingService sortingService){
this.sortingService=sortingService;
} // ...
}
声明GET方法检查服务运行状态,同时帮助探索注解在测试中的工作方式:
@GetMapping
public ResponseEntity<String> helloWorld(){
return ResponseEntity.ok("Hello, World!");
}
添加POST方法接收JSON数组并返回排序结果,测试此类方法有助于理解MockMvc
的用法:
@PostMapping
public ResponseEntity<List<Integer>> sort(@RequestBody List<Integer> arr){
return ResponseEntity.ok(sortingService.sortArray(arr));
}
4. @SpringBootTest和@WebMvcTest对比
@WebMvcTest
位于org.springframework.boot.test.autoconfigure.web.servlet
包,而@SpringBootTest
位于org.springframework.boot.test.context
包。Spring Boot默认添加必要测试依赖,类级别每次只能使用其中一个注解。
4.1. 使用MockMvc
在@SpringBootTest
上下文中,MockMvc
会直接调用控制器中的实际服务实现。服务层bean在应用上下文中可用。使用MockMvc
需添加@AutoConfigureMockMvc
注解,它会自动配置并注入mockMvc
实例:
@AutoConfigureMockMvc
@SpringBootTest
class SortingControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
}
@WebMvcTest
中,MockMvc
配合服务层的@MockBean
使用,可模拟服务响应而不调用真实服务。服务层bean不包含在应用上下文中,且默认提供@AutoConfigureMockMvc
:
@WebMvcTest
class SortingControllerUnitTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private SortingService sortingService;
}
⚠️ 注意:当@SpringBootTest
配合webEnvironment=RANDOM_PORT
使用时需谨慎,因为MockMvc
确保Web请求处理组件就绪但不启动Servlet容器,而webEnvironment=RANDOM_PORT
会尝试启动Servlet容器,两者存在冲突。
4.2. 自动配置了什么?
@WebMvcTest
自动配置以下组件:
MockMvc
实例DispatcherServlet
HandlerMapping
HandlerAdapter
ViewResolvers
- 扫描
@Controller
、@ControllerAdvice
、@JsonComponent
、Converter
、GenericConverter
、Filter
、WebMvcConfigurer
、HandlerMethodArgumentResolver
等组件
✅ 主要自动配置Web层相关组件
@SpringBootTest
加载@SpringBootApplication
(SpringBootConfiguration+EnableAutoConfiguration+ComponentScan)的所有内容,即完整应用上下文。包括:
application.properties
文件- 配置信息
- 支持
@Autowired
注入的bean
4.3. 轻量级还是重量级
@SpringBootTest
是重量级方案:
- 默认配置用于集成测试
- 包含应用上下文中所有bean
- 测试执行速度较慢
@WebMvcTest
是轻量级方案:
- 仅关注MVC层,适合单元测试
- 可限定测试特定控制器
- 应用上下文中bean数量有限
- 测试执行速度明显更快
4.4. 测试中的Web环境
真实应用通常通过http://localhost:8080
访问,测试中使用webEnvironment
模拟此场景并定义端口:
@SpringBootTest
支持两种模式:
- 模拟环境(
WebEnvironment.MOCK
) - 真实环境(
WebEnvironment.RANDOM_PORT
)
@WebMvcTest
仅提供模拟测试环境
@SpringBootTest
配合WebEnvironment
的示例:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class SortingControllerWithWebEnvironmentIntegrationTest {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Autowired
private ObjectMapper objectMapper;
}
GET方法测试用例:
@Test
void whenHelloWorldMethodIsCalled_thenReturnSuccessString() {
ResponseEntity<String> response = restTemplate.getForEntity("http://localhost:" + port + "/", String.class);
Assertions.assertEquals(HttpStatus.OK, response.getStatusCode());
Assertions.assertEquals("Hello, World!", response.getBody());
}
POST方法测试用例:
@Test
void whenSortMethodIsCalled_thenReturnSortedArray() throws Exception {
List<Integer> input = Arrays.asList(5, 3, 8, 1, 9, 2);
List<Integer> sorted = Arrays.asList(1, 2, 3, 5, 8, 9);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
ResponseEntity<List> response = restTemplate.postForEntity("http://localhost:" + port + "/",
new HttpEntity<>(objectMapper.writeValueAsString(input), headers),
List.class);
Assertions.assertEquals(HttpStatus.OK, response.getStatusCode());
Assertions.assertEquals(sorted, response.getBody());
}
4.5. 依赖处理
@WebMvcTest
不会自动检测控制器依赖,需手动模拟:
@WebMvcTest
class SortingControllerUnitTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private SortingService sortingService;
}
使用模拟bean的MockMvc
测试示例:
@Test
void whenSortMethodIsCalled_thenReturnSortedArray() throws Exception {
List<Integer> input = Arrays.asList(5, 3, 8, 1, 9, 2);
List<Integer> sorted = Arrays.asList(1, 2, 3, 5, 8, 9);
when(sortingService.sortArray(input)).thenReturn(sorted);
mockMvc.perform(post("/").contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(input)))
.andExpect(status().isOk())
.andExpect(content().json(objectMapper.writeValueAsString(sorted)));
}
❌ 未模拟服务会导致NullPointerException
4.6. 自定义能力
@SpringBootTest
自定义能力有限,而**@WebMvcTest
可精确限定测试范围**:
@WebMvcTest(SortingController.class)
class SortingControllerUnitTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private SortingService sortingService;
@Autowired
private ObjectMapper objectMapper;
}
此配置仅注册SortingController
及其依赖到应用上下文。
5. 总结
@SpringBootTest
和@WebMvcTest
各有明确用途:
**
@WebMvcTest
**:- 专注MVC层测试
- 提供特定控制器的便捷测试
- 配合
@MockBean
模拟服务层 - 执行速度快,适合单元测试
**
@SpringBootTest
**:- 加载完整应用上下文
- 包含所有组件、数据库连接等
- 适合集成测试和系统测试
- 执行速度较慢
使用MockMvc
时:
@SpringBootTest
直接调用真实服务实现@WebMvcTest
通过@MockBean
模拟服务响应
本文代码示例可在GitHub获取。