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



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

ServiceLoader.load(SomeInterface.class)

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

不幸的是,我

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

有没有一种简单的方法可以从测试中做到这一点,即在测试运行时?

您可以使用 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));
    }
}

来自ServiceLoader.load文档:

为给定服务类型创建新的服务加载程序,使用 当前线程的上下文类加载器。

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

如果用于提供程序装入的类

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

上下文类加载器

还需要加载服务类的模拟实现,然后将其作为模拟实现传递。

这样的上下文类加载器需要做两件事:

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

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

将调用移动到受保护的方法中,并在测试中重写它。这允许您在测试期间返回任何内容。

服务通常可以在运行时替换。

如果您使用的是 OSGi,则可以在用 @BeforeClass 注释的设置方法中替换服务实现,并在@AfterClass方法中取消注册模拟实现:

private ServiceRegistration m_registration;
@BeforeClass
public void setUp() {
  SomeInterface mockedService = Mockito.mock(SomeInterface.class);
  m_registration = registerService(Activator.getDefault().getBundle(), Integer.MAX_VALUE, SomeInterface.class, mockedService);
}
@AfterClass
public void tearDown() {
  if (m_registration != null) {
    unregisterService(m_registration);
  }
}
public static ServiceRegistration registerService(Bundle bundle, int ranking, Class<? extends IService> serviceInterface, Object service) {
  Hashtable<String, Object> initParams = new Hashtable<String, Object>();
  initParams.put(Constants.SERVICE_RANKING, ranking);
  return bundle.getBundleContext().registerService(serviceInterface.getName(), service, initParams);
}
public static void unregisterService(ServiceRegistration registration) {
  registration.unregister();
}

相关内容

  • 没有找到相关文章