You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在@MockBean标注的UserService中注入WalletService并保留@MockBean特性?

如何在使用@MockBean标注UserService时注入其依赖的WalletService?

这个问题我之前也碰到过,核心原因是@MockBean和@SpyBean的工作机制不一样——@MockBean会生成一个完全独立的Mock对象,Spring不会自动帮它注入依赖;而@SpyBean是基于真实Bean的代理,会保留原有的依赖注入逻辑。要在保留@MockBean的同时让WalletService正常注入,有两种实用的方案:

方案1:手动注入依赖到Mock对象

我们可以从Spring容器中获取WalletService实例(真实对象或Mock对象都可以),然后通过反射工具把它注入到@MockBean创建的UserService中。

修改后的测试类代码:

@RunWith(SpringRunner.class)
@SpringBootTest()
public class UserServiceTest {
    @MockBean
    private UserService userService;
    
    // 从Spring容器注入WalletService(可以是真实实例,也可以用@MockBean替换成Mock)
    @Autowired
    private WalletService walletService;

    @Before
    public void setup() {
        // 用Spring的ReflectionTestUtils手动注入依赖
        ReflectionTestUtils.setField(userService, "walletService", walletService);
    }

    @Test
    public void testIncreaseBalance_realMethod() {
        // 指定调用真实方法
        doCallRealMethod().when(userService).increaseBalance();
        userService.increaseBalance();
        
        // 验证WalletService的方法是否被正确调用
        verify(walletService, times(1)).increaseBalance();
    }

    @Test
    public void testIncreaseBalance_mockedMethod() {
        // 这里调用的是Mock方法,不会触发真实逻辑
        userService.increaseBalance();
        
        // 验证WalletService的方法没有被调用
        verify(walletService, never()).increaseBalance();
    }
}

说明:

  • 如果你不想用真实的WalletService,也可以把@Autowired换成@MockBean,生成一个WalletService的Mock对象注入进去,灵活度很高。
  • ReflectionTestUtils是Spring提供的工具类,专门用于测试中修改对象的私有字段,不用自己写反射逻辑。

方案2:结合Mockito的@InjectMocks(脱离Spring容器的纯Mock测试)

如果你的测试不需要完整的Spring上下文,也可以用Mockito原生的@InjectMocks和@Mock来实现依赖注入,这种方式更轻量:

修改后的测试类代码:

@RunWith(MockitoJUnitRunner.class) // 用Mockito的Runner,不用SpringRunner
public class UserServiceTest {
    @Mock
    private WalletService walletService; // Mockito生成的Mock对象

    @InjectMocks
    private UserService userService; // Mockito自动将walletService注入到userService中

    @Test
    public void testIncreaseBalance_realMethod() {
        doCallRealMethod().when(userService).increaseBalance();
        userService.increaseBalance();
        verify(walletService, times(1)).increaseBalance();
    }

    @Test
    public void testIncreaseBalance_mockedMethod() {
        userService.increaseBalance();
        verify(walletService, never()).increaseBalance();
    }
}

说明:

  • 这种方式不需要加载Spring上下文,测试速度更快,但如果你的业务逻辑依赖Spring的其他特性(比如事务、AOP等),还是方案1更合适。

为什么@MockBean会导致NullPointerException?

再补充下原理:当你用@MockBean标注UserService时,Spring会在容器中替换掉原来的UserService Bean,换成一个Mockito生成的纯Mock对象。这个Mock对象不会执行任何真实的Bean初始化逻辑,也不会自动注入它的依赖(比如WalletService),所以调用真实方法时就会出现NullPointerException。而@SpyBean是对真实UserService Bean的包装,会保留原有的依赖注入和初始化逻辑,所以不会有这个问题。

内容的提问来源于stack exchange,提问作者Nikitin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.01 00:32:30