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

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的注入优先级为:
    1. 构造器注入(优先匹配类型一致的Mock)
    2. Setter方法注入
    3. 字段注入(仅针对当前类的字段,默认不主动处理父类字段)
      因此当子类用构造器注入时,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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 17:56:05