13

このようなコードを持つレガシーJavaアプリケーションがあります

ServiceLoader.load(SomeInterface.class)

そして、このコードで使用する SomeInterface のモック実装を提供したいと考えています。私は mockito モッキングフレームワークを使用しています。

残念ながら、レガシー コードを変更することはできず、何も静的に追加したくありません (たとえば、META-INF への追加など)。

テスト内からこれを行う簡単な方法はありますか。テストの実行時?

4

4 に答える 4

8

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));
    }
}
于 2015-03-02T09:26:35.847 に答える
7

ServiceLoader.loadドキュメントから:

現在のスレッドのコンテキスト クラス ローダーを使用して、指定されたサービス タイプの新しいサービス ローダーを作成します。

そのため、テストの実行中に特別なコンテキスト クラス ローダーを使用して、META-INF/service. ServiceLoaderドキュメントの次の注記により、プロバイダー構成ファイルの検索にはコンテキスト クラス ローダーが使用されます。

プロバイダーのロードに使用されるクラス・ローダーのクラスパスにリモート・ネットワーク URL が含まれている場合、それらの URL はプロバイダー構成ファイルの検索プロセスで逆参照されます。

コンテキスト クラス ローダーは、サービス クラスのモック実装もロードする必要があります。これは、モック実装として渡されます。

このようなコンテキスト クラス ローダーは、次の 2 つのことを行う必要があります。

  • getResource*メソッドごとに要求に応じてプロバイダー構成ファイルを動的に生成する
  • 動的に生成されたプロバイダー構成ファイルで指定されたクラスである場合、メソッドごとに要求に応じて動的にクラスを生成します (たとえば、ASM ライブラリを使用)loadClass

上記のアプローチを使用すると、既存のコードを変更する必要はありません。

于 2015-03-02T09:11:30.933 に答える
2

呼び出しを保護されたメソッドに移動し、テストでオーバーライドします。これにより、テスト中に何でも返すことができます。

于 2015-03-02T08:51:34.960 に答える
-1

通常、サービスは実行時に置き換えることができます。

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();
}
于 2015-03-02T09:38:01.913 に答える