Java Mock:如何在运行时将类的所有实例替换为Mock对象?
如何在测试时替换类的所有实例为Mock对象?
当然可以做到!不过得看你用的Mock工具——Java生态里最常用的Mockito就有办法搞定这种直接new对象的场景,只是需要用到它的字节码增强功能(也就是mockito-inline)。
先说说你的问题核心
你现在的Foo类里是直接硬编码new Bar()来创建实例,这种写法绕开了依赖注入,普通的Mock方式(比如构造器/Setter注入Mock)没法直接替换它。这时候就得用能拦截构造方法调用的工具了。
解决方案:用Mockito-inline拦截构造调用
第一步:引入依赖
如果用Maven,把mockito-inline加到测试依赖里:
<dependency> <groupId>org.mockito</groupId> <artifactId>mockito-inline</artifactId> <version>4.8.1</version> <scope>test</scope> </dependency>
Gradle的话就是:
testImplementation 'org.mockito:mockito-inline:4.8.1'
第二步:编写测试代码
我们可以用mockStatic方法来拦截Bar的所有构造调用,让它返回我们预先创建的Mock实例:
import org.junit.Before; import org.junit.Test; import org.junit.After; import org.mockito.MockedStatic; import static org.mockito.Mockito.*; class TestFoo { private MockedStatic<Bar> mockedBarStatic; private Bar mockBar; @Before public void setUp() { // 先创建Bar的Mock实例 mockBar = mock(Bar.class); // 拦截Bar的构造方法,每次new Bar()都返回这个Mock mockedBarStatic = mockStatic(Bar.class); mockedBarStatic.when(Bar::new).thenReturn(mockBar); } @Test public void testFooUsesMockBar() { // 实例化Foo,此时Foo里的new Bar()会返回我们的Mock Foo foo = new Foo(); // 验证Bar的构造方法确实被调用了一次 mockedBarStatic.verify(Bar::new, times(1)); // 给Mock的GiveMe5方法指定返回值(比如让它返回10而不是默认的5) when(mockBar.GiveMe5()).thenReturn(10); // 假设Foo有个方法调用bar.GiveMe5(),比如: // public int getBarValue() { return bar.GiveMe5(); } // 这时候就能断言返回值是10了 // assertEquals(10, foo.getBarValue()); } @After public void tearDown() { // 必须关闭mockStatic,不然会影响其他测试用例 mockedBarStatic.close(); } }
额外建议:从设计上优化(更推荐)
虽然上面的方法能解决问题,但从代码设计角度来说,依赖注入才是更干净的测试方式。把Foo改成通过构造器接收Bar实例:
public class Foo { private Bar bar; // 构造器注入Bar,不再自己new public Foo(Bar bar) { this.bar = bar; } }
这样测试的时候就不用搞复杂的静态Mock了,直接传Mock进去就行:
@Test public void testFooWithInjectedMock() { Bar mockBar = mock(Bar.class); when(mockBar.GiveMe5()).thenReturn(10); Foo foo = new Foo(mockBar); // 直接测试逻辑即可,代码更简洁易维护 }
内容的提问来源于stack exchange,提问作者s5s
相关产品推荐
相关产品推荐

