我有一个遗留Java应用程序,其代码类似于此
ServiceLoader.load(SomeInterface.class)
我希望为此代码提供SomeInterface的模拟实现。我使用mockito模拟框架。
不幸的是我无法更改遗留代码,我不希望静态添加任何内容(例如,向META-INF添加内容)。
在测试中是否有一种简单的方法可以做到这一点,即。在测试的运行时间?
答案 0 :(得分:6)
来自ServiceLoader.load
文档:
使用。为给定的服务类型创建一个新的服务加载器 当前线程的上下文类加载器。
因此,您可以在测试运行期间使用特殊的上下文类加载器,它将在META-INF/service
中动态生成提供程序配置文件。由于ServiceLoader
文档中的这个注释,上下文类加载器将用于搜索提供者配置文件:
如果类加载器的类路径用于提供程序加载 包括远程网络URL,然后将取消引用这些URL 搜索提供程序配置文件的过程。
上下文类加载器还需要加载服务类的模拟实现,然后将其作为模拟实现传递。
这样的上下文类加载器需要做两件事:
getResource*
种方法loadClass
方法请求使用ASM library),如果是的话
在动态生成的提供程序中指定的类
配置文件使用上述方法,您无需更改现有代码。
答案 1 :(得分:6)
您可以使用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 :(得分:2)
将调用移动到受保护的方法并在测试中覆盖它。这允许您在测试期间返回任何内容。
答案 3 :(得分:0)
通常可以在运行时替换服务。
如果您使用的是 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();
}