1. 概要

モックフレームワークを使用した単体テストは、長い間有用な手法として認識されており、特にMockitoフレームワークが近年この市場を支配しています。

また、適切なコード設計を容易にし、パブリックAPIをシンプルにするために、いくつかの必要な機能が意図的に省略されています。 ただし、場合によっては、これらの欠点により、テスターはモックの作成を実行可能にするためだけに面倒なコードを作成する必要があります。

ここで、PowerMockフレームワークが役立ちます。

PowerMockito は、MockitoをサポートするPowerMockの拡張APIです。 これは、最終メソッド、静的メソッド、またはプライベートメソッドをモックする機能の欠如など、Mockitoの問題を克服するための簡単な方法でJavaReflectionAPIと連携する機能を提供します。

このチュートリアルでは、PowerMockito APIを紹介し、テストでどのように適用されるかを確認します。

2. PowerMockitoを使用したテストの準備

MockitoのPowerMockサポートを統合するための最初のステップは、MavenPOMファイルに次の2つの依存関係を含めることです。

<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>1.6.4</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito</artifactId>
    <version>1.6.4</version>
    <scope>test</scope>
</dependency>

次に、次の2つのアノテーションを適用して、PowerMockitoを使用するためのテストケースを準備する必要があります。

@RunWith(PowerMockRunner.class)
@PrepareForTest(fullyQualifiedNames = "com.baeldung.powermockito.introduction.*")

@PrepareForTestアノテーションのfullyQualifiedNames要素は、モックしたいタイプの完全修飾名の配列を表します。 この場合、ワイルドカード付きのパッケージ名を使用して、 PowerMockito に、com.baeldung.powermockito.introductionパッケージ内のすべてのタイプをモック用に準備するように指示します。

これで、PowerMockitoのパワーを活用する準備が整いました。

3. モッキングコンストラクタと最終メソッド

このセクションでは、 new 演算子を使用してクラスをインスタンス化し、そのオブジェクトを使用してfinalメソッドをモックするときに、実際のインスタンスではなくモックインスタンスを取得する方法を示します。

コンストラクターと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();

次のステップでは、期待値がfinalメソッドに設定されます。

when(collaborator.helloMethod()).thenReturn("Hello Baeldung!");

次に、このメソッドが実行されます。

String welcome = collaborator.helloMethod();

次のアサーションは、 helloMethodメソッドがcollaborator オブジェクトで呼び出されたことを確認し、モッキング期待値によって設定された値を返します。

Mockito.verify(collaborator).helloMethod();
assertEquals("Hello Baeldung!", welcome);

オブジェクト内のすべてのfinalメソッドではなく、特定のfinalメソッドをモックしたい場合は、 Mockito.spy(Tオブジェクト)メソッドが便利です。 これはセクション5で説明されています。

4. 静的メソッドのモック

CollaborationorWithStaticMethodsという名前のクラスの静的メソッドをモックしたいとします。

このクラスを宣言する方法は次のとおりです。

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(クラスクラス) 次のセクションで示すように、特定のものをモックする方法。

次に、期待値を設定して、呼び出されたときにメソッドが返す値を定義できます。

when(CollaboratorWithStaticMethods.firstMethod(Mockito.anyString()))
  .thenReturn("Hello Baeldung!");
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 Baeldung!", firstWelcome);
assertEquals("Hello Baeldung!", secondWelcome);

また、メソッドが呼び出された回数など、モックのメソッドの動作を確認することもできます。

この場合、 firstMethod は2回呼び出されていますが、secondMethodは呼び出されていません。

verifyStatic(Mockito.times(2));
CollaboratorWithStaticMethods.firstMethod(Mockito.anyString());
        
verifyStatic(Mockito.never());
CollaboratorWithStaticMethods.secondMethod();

注: verifyStatic メソッドは、 PowerMockito の静的メソッド検証の直前に呼び出して、後続のメソッド呼び出しが検証する必要があることを確認する必要があります。

最後に、静的 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 Baeldung!";
    }

    public final String finalMethod() {
        return "Hello Baeldung!";
    }

    private String privateMethod() {
        return "Hello Baeldung!";
    }

    public String privateMethodCaller() {
        return privateMethod() + " Welcome to the Java world.";
    }
}

上記のクラス定義でstaticMethodという名前の静的メソッドをモックすることから始めましょう。

まず、 PowerMockito APIを使用して、 CollaborationorForPartialMocking クラスを部分的にモックし、静的メソッドの期待値を設定します。

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);

次に、finalメソッドとprivateメソッドに移ります。

これらのメソッドの部分的なモックを説明するために、クラスをインスタンス化し、 PowerMockitoAPIに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);

同様のプロセスがプライベートメソッドに適用されます。 主な違いは、テストケースからこのメソッドを直接呼び出すことができないことです。

基本的に、プライベートメソッドは同じクラスの他のメソッドによって呼び出されます。 CollaborationorForPartialMocking クラスでは、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の使用方法を示します。

これらの例とコードスニペットの実装は、リンクされたGitHubプロジェクトにあります。