Spring单元测试@Spy引发空指针问题:是否需替换为@SpyBean或其他方案?
Spring单元测试@Spy引发空指针问题:是否需替换为@SpyBean或其他方案?
这个问题其实是Mockito和Spring测试注解的分工差异导致的,咱们一步步拆解来看:
你遇到的空指针是因为@Spy是Mockito的原生注解,它只会帮你创建一个SpringParent的Spy对象,但不会触发Spring的依赖注入机制——也就是说SpringParent里用@Resource标注的SpringChild字段完全不会被赋值,自然是null,调用sc.giveChild()就会报错。
下面给你两种核心解决方案,按需选择:
方案一:换成@SpyBean(推荐在Spring上下文测试场景使用)
@SpyBean是Spring Boot Test提供的注解,它的作用是:
- 先从Spring容器中获取
SpringParent的实例(或者容器里没有就创建一个) - 将这个实例包装成Spy对象
- 同时把Spy对象放回Spring容器,自动处理它的依赖注入(包括
@Resource/@Autowired标注的字段)
修改后的测试类示例:
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.SpyBean; import org.junit.Test; import static org.mockito.Mockito.verify; @SpringBootTest // 启动Spring上下文 public class InjectMock3 { @SpyBean SpringParent sp; @Test public void test() { sp.giveChild(); verify(sp).giveChild(); } }
这样SpringParent里的SpringChild会被Spring正常注入(只要SpringChild已经注册到容器中),空指针问题直接解决。如果需要MockSpringChild,还可以配合@MockBean替换容器里的对应Bean。
方案二:用Mockito的@InjectMocks手动注入依赖(适合纯单元测试)
如果你不想启动Spring上下文,只想做轻量的Mockito单元测试,可以用@InjectMocks让Mockito自动把依赖注入到Spy对象中:
修改后的测试类示例:
import org.junit.Test; import org.mockito.InjectMocks; import org.mockito.Spy; import org.mockito.Mock; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) // 用Mockito的Runner启动测试 public class InjectMock3 { @Mock // 创建SpringChild的Mock对象 SpringChild sc; @Spy @InjectMocks // 把Mock的sc注入到sp的对应字段中 SpringParent sp; @Test public void test() { // 给Mock的sc设置返回值 when(sc.giveChild()).thenReturn("mock child"); sp.giveChild(); verify(sp).giveChild(); } }
这个方案不需要启动Spring上下文,测试速度更快,但缺点是Mockito的@InjectMocks只会做简单的字段匹配注入,不会处理Spring注解的复杂逻辑(比如@Resource的名称匹配),适合测试简单的业务逻辑场景。
选择建议
- 如果测试需要依赖Spring容器的其他Bean(比如
SpringChild本身有复杂的Spring依赖),或者要验证Spring的依赖注入逻辑,优先选@SpyBean。 - 如果只是单独测试
SpringParent的业务逻辑,不需要Spring上下文,用@Spy+@InjectMocks+@Mock的组合更轻量。
备注:内容来源于stack exchange,提问作者work-in-progress
相关产品推荐
相关产品推荐

