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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 03:37:13