如何Mock方法内部初始化的对象?求技术实现建议
这确实是个挺常见的测试难题——当对象是在方法内部直接new出来的时候,常规的Mockito确实没法直接mock它。我给你几个实用的解决思路,从代码优化到工具绕过的都有:
解决方案一:重构代码(推荐,更符合设计原则)
把内部创建对象的逻辑抽成一个独立的方法,然后用Mockito的Spy来mock这个方法,让它返回我们预先准备好的mock对象。
首先修改你的业务类,把new SampleClass()的逻辑抽成一个protected方法:
public class YourClass { void myMethod(){ SampleClass sample = createSampleClass(); int count = sample.getProductCount(5L); } // 抽出来的对象创建方法 protected SampleClass createSampleClass() { return new SampleClass(); } }
然后在测试类里,用Spy来接管这个创建方法:
@Test public void testMyMethod() { // 创建被测试类的Spy实例(保留原类的大部分逻辑,只mock指定方法) YourClass yourClassSpy = Mockito.spy(new YourClass()); // 创建SampleClass的mock对象,并定义预期行为 SampleClass mockSample = Mockito.mock(SampleClass.class); Mockito.when(mockSample.getProductCount(Mockito.any(Long.class))).thenReturn(10); // 让Spy的createSampleClass方法返回我们的mock对象 Mockito.doReturn(mockSample).when(yourClassSpy).createSampleClass(); // 执行测试方法 yourClassSpy.myMethod(); // 验证mock方法是否被正确调用 Mockito.verify(mockSample).getProductCount(5L); }
这个方案的好处是代码更具可测试性,也符合开闭原则,后续扩展SampleClass的创建逻辑也更方便。
解决方案二:用PowerMock直接mock构造方法(无需修改原代码)
如果因为各种原因没法修改业务代码,PowerMock可以帮你拦截构造方法的调用,直接返回mock对象。
首先要确保项目中引入了PowerMock和Mockito的组合依赖,然后测试类需要添加特定注解:
@RunWith(PowerMockRunner.class) @PrepareForTest(YourClass.class) // 告诉PowerMock需要处理YourClass里的构造方法 public class YourClassTest { @Test public void testMyMethod() { // 创建SampleClass的mock对象并定义行为 SampleClass mockSample = Mockito.mock(SampleClass.class); Mockito.when(mockSample.getProductCount(Mockito.any(Long.class))).thenReturn(10); // 拦截SampleClass的无参构造方法,返回我们的mock对象 PowerMockito.whenNew(SampleClass.class).withNoArguments().thenReturn(mockSample); // 正常创建业务类实例并执行方法 YourClass yourClass = new YourClass(); yourClass.myMethod(); // 验证调用逻辑 Mockito.verify(mockSample).getProductCount(5L); } }
注意:PowerMock是通过修改字节码实现的,部分项目如果有严格的字节码检查可能会有兼容性问题,所以这个方案是“无法修改代码”时的备选方案。
解决方案三:依赖注入替换内部创建(最佳实践)
如果SampleClass是一个业务服务类,最推荐的方式是把它作为依赖注入到你的业务类中,而不是在方法内部直接创建。
修改业务类,通过构造方法注入SampleClass:
public class YourClass { private final SampleClass sampleClass; // 构造方法注入依赖 public YourClass(SampleClass sampleClass) { this.sampleClass = sampleClass; } void myMethod(){ int count = sampleClass.getProductCount(5L); } }
测试时直接传入mock对象即可,完全不需要额外的工具:
@Test public void testMyMethod() { SampleClass mockSample = Mockito.mock(SampleClass.class); Mockito.when(mockSample.getProductCount(5L)).thenReturn(10); // 直接把mock对象传入业务类 YourClass yourClass = new YourClass(mockSample); yourClass.myMethod(); // 验证调用 Mockito.verify(mockSample).getProductCount(5L); }
这个方案让类的依赖关系更加清晰,完全符合面向对象的依赖倒置原则,也是长期维护代码的最佳实践。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

