PowerMockの概要
1. 概要
モックフレームワークを使用した単体テストは、長い間有用な手法として認識されており、特にMockitoフレームワークが近年この市場を支配しています。
また、適切なコード設計を容易にし、パブリックAPIをシンプルにするために、いくつかの必要な機能は意図的に省略されています。 ただし、場合によっては、これらの欠点により、テスターがモックの作成を実行可能にするためだけに面倒なコードを書く必要があります。
ここで、PowerMockフレームワークが役立ちます。
PowerMockitoは、MockitoをサポートするPowerMockの拡張APIです。 Java Reflection APIと簡単に連携して、最終メソッド、静的メソッド、またはプライベートメソッドをモックする機能の欠如など、Mockitoの問題を克服する機能を提供します。
このチュートリアルでは、PowerMockito APIの概要とテストでの適用方法について説明します。
2. PowerMockitoを使用したテストの準備
MockitoのPowerMockサポートを統合する最初のステップは、Maven POMファイルに次の2つの依存関係を含めることです。
org.powermock
powermock-module-junit4
1.6.4
test
org.powermock
powermock-api-mockito
1.6.4
test
次に、次の2つのアノテーションを適用して、PowerMockitoを操作するためのテストケースを準備する必要があります。
@RunWith(PowerMockRunner.class)
@PrepareForTest(fullyQualifiedNames = "com.example.powermockito.introduction.*")
@PrepareForTestアノテーションのfullyQualifiedNames要素は、モックしたいタイプの完全修飾名の配列を表します。 この場合、ワイルドカード付きのパッケージ名を使用して、PowerMockitoに、com.example.powermockito.introductionパッケージ内のすべてのタイプをモック用に準備するように指示します。
これで、PowerMockitoの能力を活用する準備が整いました。
3. モックコンストラクタと最終メソッド
このセクションでは、new演算子を使用してクラスをインスタンス化するときに、実際のインスタンスではなくモックインスタンスを取得し、そのオブジェクトを使用して最終メソッドをモックする方法を示します。 コンストラクターとfinalメソッドがモックされるコラボレーションクラスは、次のように定義されます。
public class CollaboratorWithFinalMethods {
public final String helloMethod() {
return "Hello World!";
}
}
まず、PowerMockitoAPIを使用してモックオブジェクトを作成します。
CollaboratorWithFinalMethods mock = mock(CollaboratorWithFinalMethods.class);
次に、そのクラスの引数なしのコンストラクターが呼び出されるたびに、実際のインスタンスではなくモックインスタンスが返されることを示す期待値を設定します。
whenNew(CollaboratorWithFinalMethods.class).withNoArguments().thenReturn(mock);
デフォルトのコンストラクターを使用してCollaboratorWithFinalMethodsクラスをインスタンス化することにより、この構築モックが実際にどのように機能するかを見てから、PowerMockの動作を確認してみましょう。
CollaboratorWithFinalMethods collaborator = new CollaboratorWithFinalMethods();
verifyNew(CollaboratorWithFinalMethods.class).withNoArguments();
次のステップでは、期待値が最終メソッドに設定されます。
when(collaborator.helloMethod()).thenReturn("Hello example!");
次に、このメソッドが実行されます。
String welcome = collaborator.helloMethod();
次のアサーションは、helloMethodメソッドがcollaboratorオブジェクトで呼び出されたことを確認し、モック期待値によって設定された値を返します。
Mockito.verify(collaborator).helloMethod();
assertEquals("Hello example!", welcome);
オブジェクト内のすべての最終メソッドではなく、特定の最終メソッドをモックしたい場合は、Mockito.spy(T object)メソッドが役立つ場合があります。 これはセクション5で説明されています。
4. 静的メソッドのモック
CollaboratorWithStaticMethods.という名前のクラスの静的メソッドをモックしたいとします。このクラスは次のように宣言されています。
public class CollaboratorWithStaticMethods {
public static String firstMethod(String name) {
return "Hello " + name + " !";
}
public static String secondMethod() {
return "Hello no one!";
}
public static String thirdMethod() {
return "Hello no one again!";
}
}
これらの静的メソッドをモックするには、囲んでいるクラスをPowerMockitoAPIに登録する必要があります。
mockStatic(CollaboratorWithStaticMethods.class);
または、次のセクションで示すように、Mockito.spy(Class<T> class)メソッドを使用して特定のメソッドをモックすることもできます。
次に、呼び出されたときにメソッドが返す値を定義するために期待値を設定できます。
when(CollaboratorWithStaticMethods.firstMethod(Mockito.anyString()))
.thenReturn("Hello example!");
when(CollaboratorWithStaticMethods.secondMethod()).thenReturn("Nothing special");
または、thirdMethodメソッドを呼び出すときに例外がスローされるように設定されている場合があります。
doThrow(new RuntimeException()).when(CollaboratorWithStaticMethods.class);
CollaboratorWithStaticMethods.thirdMethod();
次は、最初の2つのメソッドを実行します。
String firstWelcome = CollaboratorWithStaticMethods.firstMethod("Whoever");
String secondWelcome = CollaboratorWithStaticMethods.firstMethod("Whatever");
実際のクラスのメンバーを呼び出す代わりに、上記の呼び出しはモックのメソッドに委任されます。 次の主張は、モックが有効になったことを証明しています。
assertEquals("Hello example!", firstWelcome);
assertEquals("Hello example!", secondWelcome);
また、メソッドが呼び出された回数など、モックのメソッドの動作を確認することもできます。 この場合、firstMethodは2回呼び出されていますが、secondMethodは一度も呼び出されていません。
verifyStatic(Mockito.times(2));
CollaboratorWithStaticMethods.firstMethod(Mockito.anyString());
verifyStatic(Mockito.never());
CollaboratorWithStaticMethods.secondMethod();
Note:PowerMockitoの静的メソッド検証の直前に、verifyStaticメソッドを呼び出して、後続のメソッド呼び出しが検証する必要があることを確認する必要があります。
最後に、静的thirdMethodメソッドは、前にモックで宣言されたようにRuntimeExceptionをスローする必要があります。 これは、@Testアノテーションのexpected要素によって検証されます。
@Test(expected = RuntimeException.class)
public void givenStaticMethods_whenUsingPowerMockito_thenCorrect() {
// other methods
CollaboratorWithStaticMethods.thirdMethod();
}
5. 部分的なモック
クラス全体をモックする代わりに、PowerMockito APIでは、spyメソッドを使用してクラスの一部をモックすることができます。 次のクラスは、部分的なモックのPowerMockサポートを説明するために共同編集者として使用されます。
public class CollaboratorForPartialMocking {
public static String staticMethod() {
return "Hello example!";
}
public final String finalMethod() {
return "Hello example!";
}
private String privateMethod() {
return "Hello example!";
}
public String privateMethodCaller() {
return privateMethod() + " Welcome to the Java world.";
}
}
上記のクラス定義でstaticMethodという名前の静的メソッドをモックすることから始めましょう。 まず、PowerMockito APIを使用してCollaboratorForPartialMockingクラスを部分的にモックし、その静的メソッドの期待値を設定します。
spy(CollaboratorForPartialMocking.class);
when(CollaboratorForPartialMocking.staticMethod()).thenReturn("I am a static mock method.");
次に、静的メソッドが実行されます。
returnValue = CollaboratorForPartialMocking.staticMethod();
モック動作は次のように検証されます。
verifyStatic();
CollaboratorForPartialMocking.staticMethod();
次のアサーションは、戻り値を期待値と比較することにより、モックメソッドが実際に呼び出されたことを確認します。
assertEquals("I am a static mock method.", returnValue);
ここで、最終メソッドおよびプライベートメソッドに移ります。 これらのメソッドの部分的なモックを説明するために、クラスをインスタンス化し、PowerMockito APIにspyに指示する必要があります。
CollaboratorForPartialMocking collaborator = new CollaboratorForPartialMocking();
CollaboratorForPartialMocking mock = spy(collaborator);
上記で作成したオブジェクトは、finalメソッドとprivateメソッドの両方のモックを示すために使用されます。 ここで、期待値を設定してメソッドを呼び出すことにより、最終メソッドを処理します。
when(mock.finalMethod()).thenReturn("I am a final mock method.");
returnValue = mock.finalMethod();
そのメソッドを部分的にモックする動作が証明されています:
Mockito.verify(mock).finalMethod();
テストでは、finalMethodメソッドを呼び出すと、期待値に一致する値が返されることを確認します。
assertEquals("I am a final mock method.", returnValue);
同様のプロセスがプライベートメソッドに適用されます。 主な違いは、テストケースからこのメソッドを直接呼び出すことができないことです。 基本的に、プライベートメソッドは同じクラスの他のメソッドによって呼び出されます。 CollaboratorForPartialMockingクラスでは、privateMethodメソッドはprivateMethodCallerメソッドによって呼び出され、後者をデリゲートとして使用します。 期待と呼び出しから始めましょう:
when(mock, "privateMethod").thenReturn("I am a private mock method.");
returnValue = mock.privateMethodCaller();
プライベートメソッドのモックが確認されます。
verifyPrivate(mock).invoke("privateMethod");
次のテストは、プライベートメソッドの呼び出しからの戻り値が期待値と同じであることを確認します。
assertEquals("I am a private mock method. Welcome to the Java world.", returnValue);
6. 結論
このチュートリアルでは、PowerMockito APIの概要を説明し、Mockitoフレームワークを使用するときに開発者が遭遇する問題のいくつかを解決するためのAPIの使用法を示します。
これらの例とコードスニペットの実装は、the linked GitHub projectにあります。