我有了代碼是這樣的在Java中,我如何模擬使用ServiceLoader加載的服務?
ServiceLoader.load(SomeInterface.class)
遺留的Java應用程序,我想提供一個模擬實現SomeInterface的此代碼使用。我使用mockito嘲笑框架。
不幸的是,我無法更改遺留代碼,也不希望靜態添加任何東西(例如添加東西到META-INF)。
有沒有一種簡單的方法可以在測試中做到這一點,即。在測試的運行時間?
我有了代碼是這樣的在Java中,我如何模擬使用ServiceLoader加載的服務?
ServiceLoader.load(SomeInterface.class)
遺留的Java應用程序,我想提供一個模擬實現SomeInterface的此代碼使用。我使用mockito嘲笑框架。
不幸的是,我無法更改遺留代碼,也不希望靜態添加任何東西(例如添加東西到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));
}
}
這可能是最好的答案,儘管我在過去操縱代碼的嘲諷框架(例如,jMockit會導致非常奇怪的行爲)方面有不好的經驗。我可能會將這些遺留代碼所調用的許多地方包含在間接調用中,這將允許我從測試中插入模擬版本的遺留代碼。 – 2015-03-02 16:17:23
@GraemeMoss:另一種想法是,如果ServiceLoader方法在構造時被調用,並且您可以在構建包含類和加載的服務類的實際用法之間「中間」,則可以使用反射來替換在實際使用之前,成員變量持有對加載的服務類的引用並引用您的模擬。 – esaj 2015-03-02 19:42:31
我曾想過這個,但是這個領域是最終的。 :-( – 2015-03-03 18:06:13
將調用移入受保護的方法並在測試中覆蓋它。這允許你在測試過程中返回任何東西。
我無法更改舊版代碼。我已經更新了我的問題。 – 2015-03-02 09:24:21
在這種情況下,'PowerMock'是你最好的選擇。請記住,將來總是要包裝靜態方法調用;他們很誘人,但是讓代碼很難或不可能測試。 – 2015-03-02 10:05:43
爲給定的服務類型新的服務加載器,使用 當前線程的上下文類加載器。
所以,你可以在試運行期間,將動態生成META-INF/service
提供者配置文件使用一個特殊的上下文類加載器。上下文類加載器將被用於搜索提供者配置文件,由於該注意ServiceLoader
文檔中:
如果用於提供者加載 類加載器的類路徑包含遠程網絡網址,然後將這些在 搜索提供程序配置文件的過程中,URL將被取消引用。
上下文類加載器還需要加載服務類的模擬實現,然後將其作爲模擬實現進行傳遞。
這樣的上下文類加載器需要做兩件事情:
getResource*
方法loadClass
方法,如果它是 類在動態生成的提供程序中指定的 配置文件使用上述方法,您不需要更改現有的代碼。
這聽起來很重。我將如何創建這樣的類加載器? – 2015-03-02 09:25:55
服務通常可以在運行時進行更換。
如果您正在使用的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();
}
OSGi!= ServiceLoader。 – bmargulies 2015-03-02 11:28:44
你能只需更改應用程序代碼爲測試目的? – SpaceTrucker 2015-03-02 09:04:12