如何在不使用@SpringBootTest的情况下轻量化测试Spring Boot控制器?
轻量化控制器测试的两种方案
1. 基于Spring Web层上下文的集成测试:@WebMvcTest
这个注解专门为Web控制器测试设计,只会加载与Web层相关的Bean(比如控制器、请求映射处理器、参数校验器等),不会加载整个应用的业务层、持久层组件,启动速度比@SpringBootTest快得多,完美解决你遇到的5秒启动耗时问题。
改造后的测试代码示例:
import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.when; @WebMvcTest(ChoreController.class) // 仅加载指定的控制器类 public class ChoreControllerTest { @Autowired private ChoreController controller; // 直接注入控制器实例 @MockBean // 模拟控制器依赖的业务服务,解决NPE问题 private ChoreService choreService; @Test public void throwOnMissingChore() { // 预设服务层的行为:当传入0L时抛出异常 when(choreService.getChore(0L)).thenThrow(new ChoreNotFoundException()); assertThrows(ChoreNotFoundException.class, () -> this.controller.getChore(0L)); } }
- 为什么不会NPE:
@MockBean会自动创建一个模拟的ChoreService实例,并注入到控制器中,填补了依赖缺失的空白。 - 额外能力:如果需要测试完整的HTTP请求流程(比如URL映射、JSON序列化、HTTP状态码返回等),还可以用
MockMvc模拟请求:@Autowired private MockMvc mockMvc; @Test public void throwOnMissingChoreViaMockMvc() throws Exception { when(choreService.getChore(0L)).thenThrow(new ChoreNotFoundException()); mockMvc.perform(get("/chores/0")) .andExpect(status().isNotFound()); }
2. 纯单元测试:完全跳过Spring上下文
如果你的测试只关注控制器内部的逻辑处理(不需要验证Web层的特性),可以用Mockito直接模拟依赖,完全不启动Spring,测试执行时间几乎可以忽略。
代码示例:
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) // 启用Mockito扩展 public class ChoreControllerTest { @Mock // 模拟控制器依赖的业务服务 private ChoreService choreService; @InjectMocks // 自动将模拟服务注入到控制器实例中 private ChoreController controller; @Test public void throwOnMissingChore() { when(choreService.getChore(0L)).thenThrow(new ChoreNotFoundException()); assertThrows(ChoreNotFoundException.class, () -> controller.getChore(0L)); } }
- 优势:完全避免Spring上下文启动的开销,测试速度最快。
- 适用场景:只验证控制器的逻辑分支、参数处理等,不需要测试Web层的请求响应流程。
测试多个控制器时的耗时优化
如果要测试多个控制器,核心思路是复用Spring上下文,避免重复加载的开销:
共享
@WebMvcTest配置:如果多个控制器的测试类使用相同的@WebMvcTest参数(比如一次性指定多个控制器),Spring会自动缓存上下文,后续测试类直接复用,不需要重新加载:@WebMvcTest({ChoreController.class, UserController.class, TaskController.class}) public class MultiControllerTest { // 在这里编写多个控制器的测试用例 }即使分开写测试类,只要每个类的
@WebMvcTest配置一致(比如没有额外的@Import或自定义配置),Spring也会自动缓存上下文,第二次及以后的测试启动速度会大幅提升。分组功能相关测试:把业务关联的控制器测试放在同一个测试类中,减少上下文的加载次数。
优先纯单元测试:对于不需要Web层特性的控制器测试,尽量用Mockito的纯单元测试方式,完全跳过Spring上下文的启动开销。
内容的提问来源于stack exchange,提问作者jon
相关产品推荐
相关产品推荐

