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

Mockito局部变量Spy使用doReturn()仍触发原方法的问题求助

问题根源与解决方案:局部变量无法被Mockito Spy/Mock的问题

这个问题其实是Mockito新手常踩的典型坑——你在测试类里用@Spy创建的ReportFileWriter实例,和被测方法someMethod里new出来的局部实例完全是两个独立的对象!

为什么当前代码不生效?

Mockito的Spy和Mock功能,只能作用于已经被注入到被测类(或者你明确传入的)的实例上。而在你的someMethod里,是直接通过new ReportFileWriter(...)创建了一个全新的局部对象,这个对象和你测试类里的Spy对象没有任何关联。Mockito根本无法拦截这种方法内部直接new出来的局部变量,自然也就没法替换它的writeResult方法行为。

两种可行的解决方案

方案1:重构被测代码,抽离对象创建逻辑(推荐)

这是最干净、符合面向设计原则的做法——把ReportFileWriter的创建逻辑抽成一个独立的方法,这样测试时就能Mock这个方法返回我们的Spy/Mock对象。

第一步,修改被测类IDataSync,新增一个protected方法负责创建ReportFileWriter:

public class IDataSync {
    // 原有的成员变量和方法...
    
    protected ReportFileWriter createReportFileWriter() {
        return new ReportFileWriter(
            environment.getProperty("reports.tempFileDir"), 
            environment.getProperty("reports.fileNameFormater.i")
        );
    }

    public void someMethod(/* 你的参数 */) {
        // 替换原来的new语句,调用抽离后的方法
        ReportFileWriter iReportFileWriter = createReportFileWriter();
        fullFilename = iReportFileWriter.writeResult(result.contactRecords, taskParameter.startDateTime);
        // 其他逻辑...
    }
}

第二步,修改测试代码,Spy被测类IDataSync,并Mock它的createReportFileWriter方法返回我们的Spy对象:

@Spy
@InjectMocks
IDataSync iDataSync ;

@Spy 
ReportFileWriter mockReportFileWriter = new ReportFileWriter(Any.stringNotNull(), Any.stringNotNull());

@Mock 
Environment environment;

@Test 
public void testRunTask() {
    // 让被测类的createReportFileWriter返回我们准备好的Spy对象
    doReturn(mockReportFileWriter).when(iDataSync).createReportFileWriter();
    
    // 现在Mock这个Spy对象的writeResult方法,就能生效了
    doReturn(Any.stringNotNull()).when(mockReportFileWriter)
        .writeResult(any(List.class), any(Date.class));
    
    iDataSync.someMethod(readParameters, false);
    
    // 可选:验证writeResult是否被正确调用
    verify(mockReportFileWriter).writeResult(any(List.class), any(Date.class));
}

方案2:使用PowerMock拦截new对象操作(无需修改被测代码)

如果你不想改动被测代码,可以用PowerMock来拦截方法内部的new操作。不过要注意,PowerMock需要额外的依赖和配置,复杂度会高一些。

第一步,添加PowerMock相关依赖(根据你的构建工具调整,比如Maven),然后在测试类上添加注解:

@RunWith(PowerMockRunner.class)
@PrepareForTest(IDataSync.class) // 必须指定要拦截new操作的类(即IDataSync)
public class DataSyncTest {

    @Mock 
    Environment environment;
    
    @Mock
    ReportFileWriter mockReportFileWriter;

    @InjectMocks
    IDataSync iDataSync ;

    @Test 
    public void testRunTask() throws Exception {
        // 拦截new ReportFileWriter的任何调用,返回我们的Mock对象
        PowerMockito.whenNew(ReportFileWriter.class)
            .withArguments(anyString(), anyString())
            .thenReturn(mockReportFileWriter);
        
        // Mock writeResult方法的返回值
        doReturn(Any.stringNotNull()).when(mockReportFileWriter)
            .writeResult(any(List.class), any(Date.class));
        
        iDataSync.someMethod(readParameters, false);
    }
}

方案对比

  • 方案1:更推荐,通过重构让代码更具可测试性,符合依赖注入的设计思想,没有额外依赖。
  • 方案2:无需修改业务代码,但引入PowerMock后,测试可能会更难维护,且在模块化项目(如Java 9+模块系统)中可能存在兼容性问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:38:26