@WebMvcTest测试中如何注入真实Service实例解决覆盖率问题
问题说明
针对MyController.java编写的@WebMvcTest测试代码如下:
@WebMvcTest(controllers = MyController.class) class MyControllerTest { @Autowired private MockMvc mockMvc; @MockBean private SomeService service; //dependency of MyService @SpyBean private MyService myService; //used in MyController @Test void testMyControllerCall() { //someTest } }
存在的现象与需求:
- 给
MyService添加@SpyBean注解时,虽然可以调用真实服务逻辑并将Bean注入MyController,但运行测试覆盖率统计时,MyService的代码不会被标记为已覆盖 - 需要在测试类中向
MyController注入常规(非Mock、非Spy)的MyService真实实现 - 不接受将
MyControllerTest移动到test/java/controller包外的方案,需要保留测试类在对应包下维持代码结构规整
当前项目目录结构:
> main/java > --/controller > ----/MyController.java > --/service > ----/MyService.java > test/java > --/controller > ----/MyControllerTest.java
解决方法
@WebMvcTest属于Web层切片测试,默认只会加载Controller层相关的Bean,Service层Bean不会被自动注册到测试上下文。之前使用@SpyBean、@MockBean注入的对象会经过Mock代理增强,覆盖率工具无法识别代理背后真实类的执行路径,因此不会统计对应代码的覆盖率。
按以下步骤修改即可:
- 移除
MyService上的@SpyBean注解 - 在测试类上添加
@Import(MyService.class)注解,显式将MyService的原生真实实现导入测试上下文 - 直接通过
@Autowired注入MyService即可,不需要添加任何Mock/Spy相关注解
修改后的测试代码示例:
@WebMvcTest(controllers = MyController.class) @Import(MyService.class) // 显式导入原生MyService真实实现,无代理增强 class MyControllerTest { @Autowired private MockMvc mockMvc; @MockBean private SomeService service; // 继续Mock MyService依赖的外部组件 @Autowired private MyService myService; // 直接注入真实实现 @Test void testMyControllerCall() { // 编写测试逻辑 } }
该方案的优势:
- 导入的
MyService是Spring初始化的原生Bean,没有Mock/Spy代理层,覆盖率工具可以正常识别代码执行路径,准确统计覆盖率 - 不需要调整测试类的存放位置,完全匹配原有目录结构规范
- 如果
MyService后续新增其他需要Mock的依赖,直接在测试类中添加对应@MockBean即可,不会影响MyService真实Bean的注入逻辑
内容的提问来源于stack exchange,提问作者Ranielle Canlas
相关产品推荐
相关产品推荐

