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

JUnit与Mockito:如何模拟带Mock参数实例化的类?

如何用Mockito模拟类内部实例化对象的返回结果?

问题场景

你的测试代码与被测试类如下:

public class TestClass {
    @Test
    public void testSomething() {
        SomethingConfig somethingConfigMock = mock(SomethingConfig.class);
        
        final ClassBeingTested cbt = new ClassBeingTested(
            parameter1,
            parameter2,
            somethingConfigMock
        );
        cbt.getResults();
    }
}

public class ClassBeingTested {

    public ClassBeingTested(parameter1, parameter2, somethingConfig) {
        this.parameter1 = parameter1;
        this.parameter2 = parameter2;
        this.somethingConfig = somethingConfig;
        // 其他初始化逻辑
    }

    public Results getResults() {
        // 其他业务逻辑
        OtherClassToTest otherClassToTest = new OtherClassToTest(somethingConfig);
        String s = otherClassToTest.getSomeString();
        // 其他业务逻辑
    }

}

你希望OtherClassToTest通过somethingConfigMock实例化后,getSomeString()等方法能返回指定结果,尝试的写法如下:

when(new OtherClassToTest(somethingConfigMock)).thenReturn(otherClassToTestMock);
when(otherClassToTestMock.getSomeString()).thenReturn("genericString");

但该写法无法生效,且你不想对现有代码做大量重构;之前仅mocksomethingConfigMock时,OtherClassToTest内部会出现空指针异常。

思路分析

你的写法存在错误:Mockito默认无法直接拦截new关键字创建对象的行为,因为new会生成一个全新的实例,和你预先mock的otherClassToTestMock完全无关,所以when(new...)的逻辑不会对实际运行的代码产生影响。

无需大量重构的解决方案

方案1:抽离对象创建逻辑,用Spy模拟(推荐)

仅需对ClassBeingTested做微小修改,把创建OtherClassToTest的逻辑抽成一个protected辅助方法:

public class ClassBeingTested {
    // 原有成员变量、构造方法不变
    
    // 抽离对象创建逻辑
    protected OtherClassToTest createOtherClassToTest(SomethingConfig config) {
        return new OtherClassToTest(config);
    }

    public Results getResults() {
        // 其他业务逻辑
        // 调用辅助方法创建对象
        OtherClassToTest otherClassToTest = createOtherClassToTest(somethingConfig);
        String s = otherClassToTest.getSomeString();
        // 其他业务逻辑
    }
}

然后在测试类中,用Mockito的spy包装被测试实例,模拟这个辅助方法的返回:

@Test
public void testSomething() {
    SomethingConfig somethingConfigMock = mock(SomethingConfig.class);
    OtherClassToTest otherClassToTestMock = mock(OtherClassToTest.class);
    // 设置mock对象的方法返回值
    when(otherClassToTestMock.getSomeString()).thenReturn("genericString");

    // 创建被测试类的Spy实例(保留原有逻辑,仅指定方法被模拟)
    ClassBeingTested cbt = spy(new ClassBeingTested(parameter1, parameter2, somethingConfigMock));
    // 让辅助方法返回我们预先mock好的对象
    doReturn(otherClassToTestMock).when(cbt).createOtherClassToTest(somethingConfigMock);

    cbt.getResults();
    // 后续添加断言逻辑
}

这种修改仅新增一个辅助方法,完全不影响原有业务逻辑,是最轻量、最易维护的方案。

方案2:用PowerMock拦截new操作(无需修改被测试类)

如果你完全不想修改被测试类,可以使用PowerMock扩展Mockito的能力,直接拦截new关键字的行为。需要先引入PowerMock的相关依赖,然后按以下方式编写测试:

// 必须添加注解,指定测试Runner和需要拦截的类
@RunWith(PowerMockRunner.class)
@PrepareForTest(ClassBeingTested.class)
public class TestClass {
    @Test
    public void testSomething() {
        SomethingConfig somethingConfigMock = mock(SomethingConfig.class);
        OtherClassToTest otherClassToTestMock = mock(OtherClassToTest.class);
        when(otherClassToTestMock.getSomeString()).thenReturn("genericString");

        // 拦截new OtherClassToTest(somethingConfigMock)的操作,返回mock对象
        PowerMockito.whenNew(OtherClassToTest.class)
                    .withArguments(somethingConfigMock)
                    .thenReturn(otherClassToTestMock);

        ClassBeingTested cbt = new ClassBeingTested(parameter1, parameter2, somethingConfigMock);
        cbt.getResults();
        // 后续添加断言逻辑
    }
}

注意:PowerMock需要特定的测试Runner和注解,会增加测试的复杂度,因此优先推荐方案1。

关于空指针异常的说明

之前仅mocksomethingConfigMock时出现NPE,是因为OtherClassToTest的构造方法或getSomeString()方法中,调用了somethingConfigMock的某些方法,但你没有为这些方法设置返回值,导致调用时返回null,进而触发空指针。使用上述两种方案后,无需关注somethingConfigMock的内部调用,直接模拟OtherClassToTest的返回即可避免该问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 21:46:38