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

如何使用Mockito模拟静态工厂方法及资源管理器方法?

如何模拟LicenseSourceImpl中resourceManager.getLicenseSources方法的调用?

问题场景

需要测试LicenseSourceImpl类中resourceManager.getLicenseSources(licenseId)的调用逻辑,目标类代码如下:

class LicenseSourceImpl{
    // ...
    ResourceManager resourceManager = ManagerFactory.getInstance(ResourceManager.class);
    List<Source> sources = resourceManager.getLicenseSources(licenseId);
    // ...
}

已尝试的失败方案

方案1:模拟ManagerFactory实例

ManagerFactory mf = Mockito.mock(ManagerFactory.class);
Mockito.when(mf.getInstance(any())).thenReturn(resourceManager);
Mockito.when(resourceManager.getLicenseSources(LICENSE_ID)).thenReturn(sources);

报错信息:

java.lang.NullPointerException: Cannot invoke "java.lang.Class.getName()" because "clazz" is null

方案2:模拟ManagerFactory并尝试静态调用

ManagerFactory mf = Mockito.mock(ManagerFactory.class);
Mockito.when(mf.getInstance(ResourceManager.class)).thenReturn(resourceManager);
Mockito.when(resourceManager.getLicenseSources(LICENSE_ID)).thenReturn(sources);

报错信息:

org.mockito.exceptions.misusing.MissingMethodInvocationException: 
when() requires an argument which has to be 'a method call on a mock'.
For example:
    when(mock.getArticles()).thenReturn(articles);

方案3:Spy工厂静态方法

ResourceManager resourceManager = spy(ManagerFactory.getInstance(ResourceManager.class));

问题:无法拦截工厂的实例创建逻辑,每次调用都会生成新的ResourceManager实例,达不到模拟效果。

可行解决方案

方案一:用Mockito的MockedStatic模拟静态工厂方法

Mockito 3.4及以上版本支持静态方法模拟,通过MockedStatic拦截ManagerFactory.getInstance的调用,让它返回我们预先造好的ResourceManager模拟实例:

// 1. 先造好ResourceManager的模拟实例,指定方法返回值
ResourceManager mockResourceManager = Mockito.mock(ResourceManager.class);
Mockito.when(mockResourceManager.getLicenseSources(LICENSE_ID)).thenReturn(expectedSources);

// 2. 静态模拟ManagerFactory,拦截getInstance调用
try (MockedStatic<ManagerFactory> mockedFactory = Mockito.mockStatic(ManagerFactory.class)) {
    mockedFactory.when(() -> ManagerFactory.getInstance(ResourceManager.class))
                 .thenReturn(mockResourceManager);

    // 3. 实例化LicenseSourceImpl并执行测试逻辑
    LicenseSourceImpl licenseSource = new LicenseSourceImpl();
    // 这里执行你的测试操作,比如调用相关业务方法,此时会使用我们模拟的resourceManager
}

注意:MockedStatic必须放在try-with-resources块中,确保测试结束后清理静态模拟,避免影响其他测试用例。

方案二:重构代码,改用依赖注入(推荐长期方案)

如果允许修改LicenseSourceImpl的代码,最优雅的方式是通过构造方法或Setter注入ResourceManager,彻底摆脱对静态工厂的直接依赖:

// 修改后的LicenseSourceImpl
class LicenseSourceImpl{
    private final ResourceManager resourceManager;

    // 构造方法注入ResourceManager
    public LicenseSourceImpl(ResourceManager resourceManager) {
        this.resourceManager = resourceManager;
    }

    // 业务方法示例
    public List<Source> getSources(String licenseId) {
        return resourceManager.getLicenseSources(licenseId);
    }
}

测试时直接注入模拟实例即可,完全不需要处理静态工厂:

// 造好ResourceManager模拟实例
ResourceManager mockResourceManager = Mockito.mock(ResourceManager.class);
Mockito.when(mockResourceManager.getLicenseSources(LICENSE_ID)).thenReturn(expectedSources);

// 注入模拟实例并执行测试
LicenseSourceImpl licenseSource = new LicenseSourceImpl(mockResourceManager);
List<Source> actualSources = licenseSource.getSources(LICENSE_ID);

// 断言结果是否符合预期
assertEquals(expectedSources, actualSources);

这种方式不仅让测试更简单,还能提升代码的扩展性和可维护性。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 12:42:36