如何在@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
相关产品推荐
相关产品推荐

