如何在Spring Bean中用Mockito仅模拟ThirdPartyService依赖?
这个问题是Spring测试里的典型场景:我们需要隔离和外部系统交互的ThirdPartyService,但希望RepositoryA、RepositoryB这些内部依赖依然由Spring正常注入,避免空指针或者真实调用外部服务的问题。你之前尝试的@Mock和@InjectMocks之所以失效,原因很明确:
- 用
@Mock+@Autowired时,@Mock只是创建了一个模拟对象,但并没有替换Spring上下文里的真实ThirdPartyService实例,所以plannerService还是会使用真实的外部服务实例。 - 用
@InjectMocks手动创建PlannerServiceImpl时,Spring不会参与这个实例的依赖注入,RepositoryA和RepositoryB自然会是空指针。
下面是两种可行的解决方案,优先推荐第一种(Spring Boot环境下):
方案一:使用Spring Boot的@MockBean(推荐)
@MockBean是Spring Boot测试框架提供的注解,它会自动将你创建的模拟对象替换掉Spring上下文中对应的真实Bean。这样既隔离了外部依赖,又能让Spring正常注入其他内部组件。
示例测试代码
import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import static org.mockito.Mockito.when; @SpringBootTest public class PlannerServiceImplTest { // 由Spring注入完整的PlannerService实例,其中ThirdPartyService会被替换为模拟对象 @Autowired private PlannerService plannerService; // 创建ThirdPartyService的模拟对象,并替换Spring上下文中的真实实例 @MockBean private ThirdPartyService thirdPartyService; @Test void testBusinessLogicWithMockedThirdParty() { // 配置模拟对象的行为:调用someExternalMethod时返回模拟结果 when(thirdPartyService.someExternalMethod()).thenReturn("模拟的外部响应"); // 调用待测试的业务方法 String businessResult = plannerService.executeBusinessLogic(); // 这里可以添加断言验证结果是否符合预期 // 比如 assertEquals("预期结果", businessResult); } }
为什么这个方案有效?
@MockBean会告诉Spring测试框架:用这个模拟对象替代上下文中的ThirdPartyService Bean。所以当PlannerServiceImpl被Spring实例化时,它的thirdPartyService字段会注入我们的模拟对象,而repositoryA和repositoryB依然会从Spring上下文获取真实的实例(如果是测试环境,通常是连接内存数据库的Repository实现,比如H2)。
方案二:普通Spring项目(非Spring Boot)的替代方案
如果你的项目不是Spring Boot,可以通过自定义测试配置类,手动注册模拟的ThirdPartyService Bean,同时让Spring加载其他真实依赖:
示例测试代码
import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.test.context.ContextConfiguration; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @ContextConfiguration(classes = {PlannerServiceImpl.class, RepositoryA.class, RepositoryB.class, TestConfig.class}) public class PlannerServiceImplTest { @Autowired private PlannerService plannerService; @Autowired private ThirdPartyService thirdPartyService; @Test void testBusinessLogic() { // 配置模拟对象行为 when(thirdPartyService.someExternalMethod()).thenReturn("模拟响应"); // 执行测试逻辑 String result = plannerService.executeBusinessLogic(); // 断言验证 } // 自定义测试配置类,注册模拟的ThirdPartyService @Configuration static class TestConfig { @Bean public ThirdPartyService thirdPartyService() { return mock(ThirdPartyService.class); } } }
说明
这个方案通过@ContextConfiguration指定需要加载的Bean和测试配置类,测试配置类中用Mockito.mock()创建模拟对象并注册为Spring Bean,这样Spring会用这个模拟对象注入到PlannerServiceImpl中,其他依赖依然是真实的Spring管理实例。
内容的提问来源于stack exchange,提问作者suraj bahl

