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
相关产品推荐
相关产品推荐

