如何使用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
相关产品推荐
相关产品推荐

