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

如何在JUnit+Mockito中Mock带@Inject@Any的Instance<SomeInterface>字段

正确Mock CDI Instance的单元测试方案

你遇到的问题核心在于:CDI的Instance<SomeInterface>是特殊的接口,它的迭代行为需要我们显式Stub,另外如果@InjectMocks未正确注入字段,也会触发NPE。下面是无需新增测试专用方法的优雅解决方案:

步骤1:正确Mock Instance及依赖

我们需要MockInstance<SomeInterface>,同时Stub它的iterator()方法,让它返回包含Mock的SomeInterface实现类的迭代器。另外如果@InjectMocks注入失败,还可以用反射手动补全字段注入(你的Mockito版本中@InjectMocks大概率能正常工作)。

完整测试代码示例

@RunWith(MockitoJUnitRunner.class)
public class SomeTestClass {

    @InjectMocks
    private AnotherClass anotherClass;

    @Mock
    private Instance<SomeInterface> impls;

    // Mock两个实现类实例
    @Mock
    private SomeInterface mockImplA;
    @Mock
    private SomeInterface mockImplB;

    @Test
    public void testSomeMethod_AllImplsReturnFalse_ReturnsNull() {
        // 让Instance返回包含两个mock实现的迭代器
        when(impls.iterator()).thenReturn(Arrays.asList(mockImplA, mockImplB).iterator());
        
        // Stub每个实现的oneMethod()返回false
        when(mockImplA.oneMethod()).thenReturn(false);
        when(mockImplB.oneMethod()).thenReturn(false);

        SomeInterface result = anotherClass.someMethod();
        
        Assert.assertNull(result);
    }

    @Test
    public void testSomeMethod_FirstImplReturnsTrue_ReturnsFirstImpl() {
        when(impls.iterator()).thenReturn(Arrays.asList(mockImplA, mockImplB).iterator());
        
        // 让第一个实现返回true
        when(mockImplA.oneMethod()).thenReturn(true);
        when(mockImplB.oneMethod()).thenReturn(false);

        SomeInterface result = anotherClass.someMethod();
        
        Assert.assertEquals(mockImplA, result);
    }
}

为什么之前的做法失败?

  1. 未Stub Instance的迭代行为:你之前的测试只Mock了Instance对象,但没有Stubiterator()方法。Mockito默认会让Mock对象的方法返回null,当代码执行for(SomeInterface imp : impls)时,调用impls.iterator()得到null,进而抛出NPE(你可能误以为是impls为null,实际是迭代器为null)。
  2. @Spy的误用:@Spy用于包装真实对象,但Instance是CDI接口,没有可用的真实实例可以Spy,所以应该用@Mock来创建纯粹的Mock对象。

备用方案:手动注入字段

如果@InjectMocks因为类代理等原因无法注入impls字段,可以用ReflectionTestUtils手动设置(需要引入spring-test依赖,或者自行编写反射工具类):

@Before
public void setup() {
    ReflectionTestUtils.setField(anotherClass, "impls", impls);
}

这样就能在不修改业务代码的前提下,完成对AnotherClass的单元测试。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:39:55