在Java中,如何模拟使用ServiceLoader加载的服务?

2022-09-04 20:38:12

我有一个遗留的Java应用程序,它具有类似这样的代码

ServiceLoader.load(SomeInterface.class)

我想提供SomeInterface的模拟实现供此代码使用。我使用模拟模拟框架。

不幸的是,我无法更改遗留代码,并且我不希望静态添加任何内容(例如,向META-INF添加内容)。

有没有一种简单的方法可以从测试中执行此操作,即。在测试运行时?


答案 1

您可以将PowerMockito与Mockito一起使用来模拟静态方法:

@RunWith(PowerMockRunner.class)
@PrepareForTest(ServiceLoader.class)
public class PowerMockingStaticTest
{
    @Mock
    private ServiceLoader mockServiceLoader;

    @Before
    public void setUp()
    {
        PowerMockito.mockStatic(ServiceLoader.class);
        Mockito.when(ServiceLoader.load(Mockito.any(Class.class))).thenReturn(mockServiceLoader);
    }

    @Test
    public void test()
    {
        Assert.assertEquals(mockServiceLoader, ServiceLoader.load(Object.class));
    }
}

答案 2

来自 ServiceLoader.load 文档:

使用当前线程的上下文类装入器为给定的服务类型创建新的服务装入器。

因此,您可以在测试运行期间使用特殊的上下文类装入器,该装入器将在 中动态生成提供程序配置文件。上下文类装入器将用于搜索提供程序配置文件,因为文档中有以下说明:META-INF/serviceServiceLoader

如果用于提供程序加载的类装入器的类路径包括远程网络 URL,则在搜索提供程序配置文件的过程中将取消引用这些 URL。

上下文类装入器还需要装入服务类的模拟实现,然后将其作为模拟实现传递。

这样的上下文类装入器需要做两件事:

  • 根据每个方法的请求动态生成提供程序配置文件getResource*
  • 根据每个方法的请求动态生成类(例如,使用 ASM 库),如果它是在动态生成的提供程序配置文件中指定的类loadClass

使用上述方法,您无需更改现有代码。


推荐