@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注入到被测类,默认不会直接使用测试类中声明的空@SpyList实例。
修复方案
两种方案选其一即可:
- 保留
@Spy注解的场景:不要给字段重新赋值,直接操作Mockito初始化好的Spy对象填充元素@BeforeEach void setUp() { // 直接往Mockito生成的Spy列表中添加元素,禁止重新给字段赋值 dependency1.add(metric1); dependency1.add(metric2); } - 不需要对List本身做方法验证的场景:直接删掉
dependency1字段上的@Spy注解,不需要手动初始化List,Mockito会自动将测试类中的metric1、metric2两个Mock实例组装成List,注入到Service的dependency1字段中。
注意:所有被
@Mock、@Spy、@InjectMocks修饰的字段,禁止在@BeforeEach或测试方法中通过=直接重新赋值,否则会覆盖Mockito生成的代理对象,导致注入、验证逻辑全部失效。如果需要修改对象内部状态,直接调用对象的对应方法即可。
内容的提问来源于stack exchange,提问作者tusharRawat
相关产品推荐
相关产品推荐

