1. 概要

テストを作成するときに、静的メソッドをモックする必要がある状況に遭遇することがよくあります。 Mockitoのバージョン3.4.0より前は、静的メソッドを直接モックすることはできませんでした—PowerMockitoの助けを借りてのみ。

このチュートリアルでは、最新バージョンのMockitoを使用して静的メソッドをモックする方法を見ていきます。

Mockitoを使用したテストの詳細については、包括的なMockitoシリーズをご覧ください。

2. 単純な静的ユーティリティクラス

テストの焦点は、単純な静的ユーティリティクラスになります。

public class StaticUtils {

    private StaticUtils() {}

    public static List<Integer> range(int start, int end) {
        return IntStream.range(start, end)
          .boxed()
          .collect(Collectors.toList());
    }

    public static String name() {
        return "Baeldung";
    }
}

デモンストレーションの目的で、いくつかの引数を持つ1つのメソッドと、単にStringを返す別のメソッドがあります。

3. 依存関係

mockito-inline依存関係をpom.xmlに追加することから始めましょう。

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-inline</artifactId>
    <version>3.8.0</version>
    <scope>test</scope>
</dependency>

ドキュメントのように、この機能は、より馴染みのあるmockito-core依存関係に統合される可能性があることに注意してください。

4. 静的メソッドのテストに関する簡単な説明

一般的に言って、クリーンなオブジェクト指向コードを書くとき、静的クラスをモックする必要はないと言う人もいるかもしれません。 これは通常、アプリケーションの設計上の問題やコードの臭いを示唆している可能性があります。

なんで? 第一に、静的メソッドに依存するクラスは密結合であり、第二に、ほとんどの場合、テストが困難なコードにつながります。 理想的には、クラスはその依存関係を取得する責任を負わないようにする必要があり、可能であれば、外部から注入する必要があります。

したがって、コードをリファクタリングしてテスト可能にすることができるかどうかを常に調査する価値があります。もちろん、これが常に可能であるとは限らず、静的メソッドをモックする必要がある場合もあります。

5. 引数なしの静的メソッドをモックする

先に進んで、StaticUtilsクラスからnameメソッドをモックする方法を見てみましょう。

@Test
void givenStaticMethodWithNoArgs_whenMocked_thenReturnsMockSuccessfully() {
    assertThat(StaticUtils.name()).isEqualTo("Baeldung");

    try (MockedStatic<StaticUtils> utilities = Mockito.mockStatic(StaticUtils.class)) {
        utilities.when(StaticUtils::name).thenReturn("Eugen");
        assertThat(StaticUtils.name()).isEqualTo("Eugen");
    }

    assertThat(StaticUtils.name()).isEqualTo("Baeldung");
}

前述のように、Mockito 3.4.0以降、 Mockito.mockStatic(クラス classToMock) 静的メソッド呼び出しへの呼び出しをモックするメソッド。 このメソッドは、スコープ付きモックオブジェクトであるタイプのMockedStaticオブジェクトを返します。

したがって、上記の単体テストでは、Utilities変数はスレッドローカルの明示的なスコープを持つモックを表します。 スコープ付きモックはモックをアクティブ化するエンティティによって閉じられる必要があることに注意することが重要です。これが、モックがtry-with-resourcesコンストラクト内でモックを定義する理由です。スコープブロックが終了すると、自動的に閉じられます。

これは、静的モックが一時的なものであることを保証するため、特に優れた機能です。 ご存知のように、テストの実行中に静的メソッド呼び出しをいじっている場合、テストの実行は同時かつ順次であるため、テスト結果に悪影響が及ぶ可能性があります。

これに加えて、もう1つの優れた副作用は、Mockitoがすべてのテストでクラスローダーを置き換える必要がないため、テストが非常に高速に実行されることです。

この例では、スコープブロックの前後で、静的メソッドnameが実際の値を返すことを確認することでこの点を繰り返します。

6. 引数を使用して静的メソッドをモックする

次に、引数を持つメソッドをモックする必要がある場合の別の一般的なユースケースを見てみましょう。

@Test
void givenStaticMethodWithArgs_whenMocked_thenReturnsMockSuccessfully() {
    assertThat(StaticUtils.range(2, 6)).containsExactly(2, 3, 4, 5);

    try (MockedStatic<StaticUtils> utilities = Mockito.mockStatic(StaticUtils.class)) {
        utilities.when(() -> StaticUtils.range(2, 6))
          .thenReturn(Arrays.asList(10, 11, 12));

        assertThat(StaticUtils.range(2, 6)).containsExactly(10, 11, 12);
    }

    assertThat(StaticUtils.range(2, 6)).containsExactly(2, 3, 4, 5);
}

ここでは、同じアプローチに従いますが、今回はwhen句内でラムダ式を使用し、モックしたい引数とともにメソッドを指定します。非常に簡単です。

7. 結論

この簡単な記事では、Mockitoを使用して静的メソッドをモックする方法の例をいくつか見てきました。 要約すると、Mockitoは、1つの小さなラムダを介してモックされた静的オブジェクトのより狭いスコープを使用する優雅なソリューションを提供します。

いつものように、記事の完全なソースコードは、GitHubから入手できます。