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

@InjectMocks无法注入@Spy修饰的List类型依赖问题排查

问题复现

Service.java(被测类)

class Service {
    @Autowired
    private List<Metric> dependency1;
    @Autowired
    private Executor dependency2;
}

Metric.java(接口与实现)

interface Metric{
    void fetchMetric();
}

class Metric1 implements Metric{
    public void fetchMetric() {}
}

class Metric2 implements Metric{
    public void fetchMetric() {}
}

ServiceTest.java(测试类)

@ExtendWith(MockitoExtension.class)
class ServiceTest {
    @Spy
    private List<Metric> dependency1;
    @Mock
    private Executor dependency2;
    
    @InjectMocks // 被测类实例,持有上述两个依赖
    Service service;

    @Mock
    private Metric1 metric1;
    @Mock
    private Metric2 metric2;

    @BeforeEach
    void setUp() {
        // 使用Mock对象初始化Spy列表
        this.dependency1 = Arrays.asList(metric1,
                metric2
               );
    }

    @Test
    void someTest() {
        // 调试时发现标注为@Spy的dependency1,与Service实例中持有的dependency1为不同对象,预期二者应为同一实例
    }
}
核心问题

@InjectMocks没有注入预期的List实例,测试类中@Spy修饰的dependency1和Service实例中持有的dependency1是两个独立对象,导致测试失败。

是否不应该在@BeforeEach方法中完成Spy对象的初始化操作?

故障原因

两个使用错误共同导致了这个问题:

  • 直接给@Spy/@Mock修饰的字段重新赋值,破坏了Mockito的代理逻辑
    @Spy标记的字段会在测试初始化阶段被Mockito生成代理对象,@InjectMocks执行注入时拿到的就是这个初始化阶段生成的代理引用。在@BeforeEach中通过this.dependency1 = Arrays.asList(...)给字段重新赋值,只是把测试类中该字段的指向改成了新的普通List对象,此时@InjectMocks已经完成了注入,Service实例中持有的还是最开始那个空的Spy代理List,二者自然不是同一个对象。
  • Mockito对集合类型依赖有特殊注入规则
    当被测类需要注入List<接口类型>形式的依赖时,Mockito会自动扫描测试类中所有该接口的Mock/Spy实现,自行构造一个List注入到被测类,默认不会直接使用测试类中声明的空@Spy List实例。
修复方案

两种方案选其一即可:

  1. 保留@Spy注解的场景:不要给字段重新赋值,直接操作Mockito初始化好的Spy对象填充元素
    @BeforeEach
    void setUp() {
        // 直接往Mockito生成的Spy列表中添加元素,禁止重新给字段赋值
        dependency1.add(metric1);
        dependency1.add(metric2);
    }
    
  2. 不需要对List本身做方法验证的场景:直接删掉dependency1字段上的@Spy注解,不需要手动初始化List,Mockito会自动将测试类中的metric1、metric2两个Mock实例组装成List,注入到Service的dependency1字段中。

注意:所有被@Mock、@Spy、@InjectMocks修饰的字段,禁止在@BeforeEach或测试方法中通过=直接重新赋值,否则会覆盖Mockito生成的代理对象,导致注入、验证逻辑全部失效。如果需要修改对象内部状态,直接调用对象的对应方法即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 12:03:25