Mockito注入Mock为null:Mockito模拟与Spring真实Bean创建的差异
Mockito与Spring依赖注入的差异分析
问题背景
测试继承抽象类的FooService时遇到依赖注入问题:抽象类通过@Autowired注入FooRepository,子类FooService用构造器注入BarService。使用Mockito测试时,抽象类中的fooRepository始终为null;但将子类构造器注入改为@Autowired字段注入后,Mock的fooRepository能正常注入。而在Spring上下文环境中,两种注入方式都能正常工作,因此需要理清Mockito与Spring创建对象的核心区别。
相关代码
抽象类与Service实现类
public abstract class MyFooServiceAbstractClass { @Autowired private FooRepository fooRepository; public void save(FooEntity foo){fooRepository.save(foo);} } @Service public class FooService extends MyFooServiceAbstractClass { private final BarService barService; public FooService(BarService barservice){ this.barService = barservice; } public void create(FooEntity fooEntity){ barService.returnSomething(); save(fooEntity); } }
Mockito测试代码
@ExtendWith(MockitoExtension.class) public class FooServiceTest { @InjectMocks private FooService fooService; @Mock private FooRepository fooRepository; @Mock private BarService barService; @Captor private ArgumentCaptor<FooEntity> captor; @Test void test(){ // Given FooEntity foo = new FooEntity(); when(barService.doSomething()).thenReturn(something); // When fooService.save(foo); // Then verify(fooRepository).save(captor.capture()); // assert goes here... } }
核心差异分析
1. 对象创建与注入的范围不同
- Spring容器:创建Bean时会处理整个类继承链的依赖。无论是父类的
@Autowired字段,还是子类的构造器/字段注入,Spring都会通过容器查找匹配的Bean,甚至会通过反射突破私有字段的访问限制,完成全层级的依赖注入。 - Mockito @InjectMocks:默认优先使用子类的构造器注入创建实例。当子类有显式构造器时,Mockito只会注入构造器中声明的依赖(即
BarService),不会主动处理父类的注解型依赖——它的注入逻辑仅聚焦当前类(子类)的注入点,不会递归解析父类的@Autowired注解。
2. 依赖注入的触发逻辑不同
- Spring:依赖注入是容器的核心能力,会扫描类上所有
@Autowired、@Resource等注解,不管是父类还是子类的字段、方法,都会统一处理。即使子类用构造器注入,Spring也会继续完成父类的字段注入。 - Mockito:
@InjectMocks的注入优先级为:- 构造器注入(优先匹配类型一致的Mock)
- Setter方法注入
- 字段注入(仅针对当前类的字段,默认不主动处理父类字段)
因此当子类用构造器注入时,Mockito完成构造器注入后就停止流程,不会处理父类的fooRepository字段,导致其为null。
3. 子类改为@Autowired注入后生效的原因
当子类把构造器注入改成@Autowired字段注入时,Mockito的@InjectMocks会触发字段注入逻辑。此时它会通过反射遍历当前类及父类的所有字段,找到类型匹配的Mock(FooRepository)并完成注入——这不是因为父类的@Autowired注解,而是Mockito字段注入阶段会扫描继承链的字段。
替代解决方案
如果不想修改子类的注入方式,可以通过反射手动给父类字段赋值:
@BeforeEach void setUp() { ReflectionTestUtils.setField(fooService, "fooRepository", fooRepository); }
借助Spring的ReflectionTestUtils工具类,直接通过反射给父类的私有字段注入Mock实例,即可解决问题。
内容的提问来源于stack exchange,提问作者kasualnetuser
相关产品推荐
相关产品推荐

