私はプロジェクトの単体テストを実装しようとしています。これは、静的メソッドが散らばっているレガシーの「ユーティリティ」プロジェクトを使用しており、クラスの多くがfinalであるか、それらのメソッドがfinalです。レガシープロジェクトを更新できません。
JMockとEasyMockはどちらも最終的なメソッドに問題を抱えており、静的呼び出しをテストするための良い方法はありません。これらをテストするためのテクニックは何ですか?
コードをリファクタリングできる場合は、final/staticメソッドへの呼び出しを単純なインスタンスメソッドにラップできます。次に例を示します。
protected Foo doBar(String name) {
return Utility.doBar(name);
}
これにより、ユニットテストでラッパーメソッドをオーバーライドして、Fooのモックインスタンスを返すことができます。
または、 Powermock を使用して、Easymock(およびMockito)を拡張し、最終メソッドと静的メソッドのモッキングを許可することもできます。
PowerMockは、EasyMockなどの他のモックライブラリをより強力な機能で拡張するフレームワークです。 PowerMockは、カスタムクラスローダーとバイトコード操作を使用して、静的メソッド、コンストラクター、最終クラスとメソッド、プライベートメソッド、静的初期化子の削除などのモックを有効にします。
以下は example のテストで、静的なfinalメソッドをモックしています。この例は、他のいくつかのタイプもモックする方法を示しています。
@Test
public void testMockStaticFinal() throws Exception {
mockStatic(StaticService.class);
String expected = "Hello altered World";
expect(StaticService.sayFinal("hello")).andReturn("Hello altered World");
replay(StaticService.class);
String actual = StaticService.sayFinal("hello");
verify(StaticService.class);
assertEquals("Expected and actual did not match", expected, actual);
// Singleton still be mocked by now.
try {
StaticService.sayFinal("world");
fail("Should throw AssertionError!");
} catch (AssertionError e) {
assertEquals("\n Unexpected method call sayFinal(\"world\"):",
e.getMessage());
}
}
間接性/依存性注入のレベルはどうですか?
レガシーユーティリティプロジェクトは依存関係であるため、コードから分離するためのインターフェイスを作成します。これで、このインターフェースの実際の/本番実装は、レガシーユーティリティメソッドに委譲されます。
public LegacyActions : ILegacyActions
{
public void SomeMethod() { // delegates to final/static legacy utility method }
}
テストでは、このインターフェースのモックを作成し、レガシーユーティリティとのやり取りを回避できます。
JMockit を使用すると、静的メソッドと最終クラスをモックできます。私は実際には調べていませんが、classloadin-fuを使用していると思います。
JMockit Expectations APIを使用すると、あらゆる種類のメソッド呼び出し(インターフェース、抽象クラス、具体的な最終クラスまたは非最終クラス、静的メソッド)に加えて、コンストラクターによるクラスのインスタンス化に期待値を設定できます。
すでに指摘したように、 JMockit を使用できます。例:
@Test
public void mockStaticAndFinalMethods(@Mocked LegacyService mock) {
new Expectations() {{
LegacyService.staticMethod("hello"); result = "Hello altered World";
}};
String actual = LegacyService.staticMethod("hello");
new LegacyService().finalMethod(123, "test");
assertEquals("Hello altered World", actual);
new Verifications() {{
mock.finalMethod(123, "test"); // verify this call occurred at least once
}};
}
リファクタリング不可能なメソッドでJNDIなどを使用して別のサービスに接続する場合は、JDNIサービスを開始して、ユーザーが制御するスタブをそのサービスに追加することを検討します。それは苦痛ですが、比較的簡単です。これは、データベースやJMSリスナーなどをセットアップすることを意味する場合がありますが、軽量のJava実装を使用すると、テストにドロップできます。