1. 概要

多くの場合、モックオブジェクトに対してMockitoによって提供されるデフォルト設定で十分です。

ただし、モックの作成中に追加のモック設定を提供する必要がある場合があります。 これは、デバッグ、レガシーコードの処理、またはいくつかのコーナーケースをカバーするときに役立つ場合があります。

前のチュートリアルでは、寛大なモックの操作方法を学びました。 このクイックチュートリアルでは、MockSettingsインターフェイスが提供するその他の便利な機能の使用方法を学習します。

2. モック設定

簡単に言うと、 MockSettingsインターフェイスはFluentAPI を提供し、モックの作成中にモック設定を簡単に追加および組み合わせることができます。

モックオブジェクトを作成すると、すべてのモックに一連のデフォルト設定が適用されます。 簡単な模擬例を見てみましょう。

List mockedList = mock(List.class);

舞台裏では、Mockito mock メソッドは、モックのデフォルト設定のセットを使用して、別のオーバーロードされたメソッドに委任します。

public static <T> T mock(Class<T> classToMock) {
    return mock(classToMock, withSettings());
}

デフォルト設定を見てみましょう。

public static MockSettings withSettings() {
    return new MockSettingsImpl().defaultAnswer(RETURNS_DEFAULTS);
}

ご覧のとおり、モックオブジェクトの標準設定セットは非常に単純です。 模擬インタラクションのデフォルトの回答を設定します。 通常、 RETURNS_DEFAULTS を使用すると、空の値が返されます。

これから取り除く重要なポイントは、必要に応じて、モックオブジェクトに独自のカスタム設定のセットを提供できることです

次のセクションでは、これが役立つ場合の例をいくつか示します。

3. 別のデフォルトの回答を提供する

モック設定についてもう少し理解できたので、モックオブジェクトのデフォルトの戻り値を変更する方法を見てみましょう。

モックのセットアップが非常に簡単だと想像してみましょう。

PizzaService service = mock(PizzaService.class);
Pizza pizza = service.orderHouseSpecial();
PizzaSize size = pizza.getSize();

このコードを期待どおりに実行すると、スタブされていないメソッドorderHouseSpecialnullを返すため、NullPointerExceptionが発生します。

これは問題ありませんが、レガシーコードを使用する場合、モックオブジェクトの複雑な階層を処理する必要があり、これらのタイプの例外が発生する場所を見つけるのに時間がかかる場合があります。

これに対抗するために、モック作成中にモック設定を介して異なるデフォルトの回答を提供できます

PizzaService pizzaService = mock(PizzaService.class, withSettings().defaultAnswer(RETURNS_SMART_NULLS));

RETURNS_SMART_NULLS をデフォルトの回答として使用することにより、Mockitoは、誤ったスタブが発生した場所を正確に示す、はるかに意味のあるエラーメッセージを表示します。

org.mockito.exceptions.verification.SmartNullPointerException: 
You have a NullPointerException here:
-> at com.baeldung.mockito.mocksettings.MockSettingsUnitTest.whenServiceMockedWithSmartNulls_thenExceptionHasExtraInfo(MockSettingsUnitTest.java:45)
because this method call was *not* stubbed correctly:
-> at com.baeldung.mockito.mocksettings.MockSettingsUnitTest.whenServiceMockedWithSmartNulls_thenExceptionHasExtraInfo(MockSettingsUnitTest.java:44)
pizzaService.orderHouseSpecial();

これにより、テストコードをデバッグする際の時間を大幅に節約できます。 Answers 列挙は、他のいくつかの事前構成された模擬回答も提供します。

  • RETURNS_DEEP_STUBS ディープスタブを返す回答これは、FluentAPIを操作するときに役立ちます。
  • RETURNS_MOCKS –この回答を使用すると、空のコレクションや空の文字列などの通常の値が返され、その後、モックが返されます。
  • CALLS_REAL_METHODS –名前が示すように、この実装を使用すると、スタブされていないメソッドが実際の実装に委任されます

4. モックの命名と詳細なロギング

MockSettingsnameメソッドを使用して、モックに名前を付けることができます。 これは、提供する名前がすべての検証エラーで使用されるため、デバッグに特に役立ちます。

PizzaService service = mock(PizzaService.class, withSettings()
  .name("pizzaServiceMock")
  .verboseLogging()
  .defaultAnswer(RETURNS_SMART_NULLS));

この例では、メソッド verboseLogging()を使用して、この命名機能を冗長ロギングと組み合わせます。

このメソッドを使用すると、このモックでのメソッド呼び出しの標準出力ストリームへのリアルタイムロギングが可能になります。 同様に、モックとの誤った相互作用を見つけるために、テストのデバッグ中に使用できます。

テストを実行すると、コンソールに次の出力が表示されます。

pizzaServiceMock.orderHouseSpecial();
   invoked: -> at com.baeldung.mockito.mocksettings.MockSettingsUnitTest.whenServiceMockedWithNameAndVerboseLogging_thenLogsMethodInvocations(MockSettingsUnitTest.java:36)
   has returned: "Mock for Pizza, hashCode: 366803687" (com.baeldung.mockito.fluentapi.Pizza$MockitoMock$168951489)

@Mock アノテーションを使用している場合、モックは自動的にフィールド名をモック名として使用することに注意してください。

5. 追加のインターフェイスをあざける

場合によっては、モックが実装する必要のある追加のインターフェイスを指定したい場合があります。 繰り返しますが、これは、リファクタリングできないレガシーコードを操作するときに役立つ場合があります。

特別なインターフェースがあると想像してみましょう。

public interface SpecialInterface {
    // Public methods
}

そして、このインターフェースを使用するクラス:

public class SimpleService {

    public SimpleService(SpecialInterface special) {
        Runnable runnable = (Runnable) special;
        runnable.run();
    }
    // More service methods
}

もちろん、これはクリーンコードではありませんが、このための単体テストを作成せざるを得ない場合は、おそらく問題が発生します。

SpecialInterface specialMock = mock(SpecialInterface.class);
SimpleService service = new SimpleService(specialMock);

このコードを実行すると、ClassCastExceptionが発生します。 これを修正するために、extraInterfacesメソッドを使用して複数のタイプでモックを作成できます。

SpecialInterface specialMock = mock(SpecialInterface.class, withSettings()
  .extraInterfaces(Runnable.class));

これで、モック作成コードは失敗しませんが、宣言されていない型へのキャストはクールではないことを強調する必要があります。

6. コンストラクター引数の提供

この最後の例では、 MockSettings を使用して、引数値を持つ実際のコンストラクターを呼び出す方法を示します。

@Test
public void whenMockSetupWithConstructor_thenConstructorIsInvoked() {
    AbstractCoffee coffeeSpy = mock(AbstractCoffee.class, withSettings()
      .useConstructor("espresso")
      .defaultAnswer(CALLS_REAL_METHODS));

    assertEquals("Coffee name: ", "espresso", coffeeSpy.getName());
}

今回、Mockitoは、 AbstractCoffee モックのインスタンスを作成するときに、String値でコンストラクターを使用しようとします。 また、実際の実装に委任するデフォルトの回答を構成します。

これは、コンストラクター内にテストまたはトリガーしてクラスを特定の状態のままにするロジックがある場合に役立つ可能性があります。 抽象クラスでスパイするときにも役立ちます。

7. 結論

このクイックチュートリアルでは、追加のモック設定を使用してモックを作成する方法を説明しました。

ただし、これは便利な場合もあり、避けられない場合もありますが、ほとんどの場合、単純なモックを使用して簡単なテストを作成するように努める必要があります。

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