きり丸の技術日記

技術検証したり、資格等をここに残していきます。

メソッド呼出回数によって返却値を変更する(正常と例外)【JavaのMockito】

Mockitoを使用して、モックにしたメソッドの呼出回数によって正常な値と例外を返却する方法を残します。

環境

  • Java
    • 17
  • org.mockito.junit.jupiter
    • 4.0.0

ユースケース

  • 1

対応

次の現在日付を返却するメソッドをモックにします。

public interface OffsetDateTimeResolver{
  default OffsetDateTime now() {
    return OffsetDateTime.now(ZoneId.of("Asia/Tokyo"));
  }
}

Mockito.whenを使用して、パラメータにてモック対象のメソッドを呼び出します。

そのままメソッドチェインでthenReturnを使用すると、正常系の値を返却します。第二パラメータ以降を可変長引数で渡している場合、パラメータの順番と呼出回数による返却値が一致します。

同様にthenThrowを使用すると、異常系の値を返却します。可変長引数も同様の挙動です。

次のコードの例は1回目が正常系、2回目が正常系、3回目が異常系、4回目が正常系、5回目が異常系を返却するテストです。

  @Mock
  private OffsetDateTimeResolver offsetDateTimeResolver;

  @DisplayName("1回目OK, 2回目OK, 3回目NG, 4回目OK, 5回目NG")
  @Test
  void _1OK_2OK_3NG_4OK_5NG() {
    // GIVEN
    OffsetDateTime now = OffsetDateTime.now(); // 現在時刻
    when(offsetDateTimeResolver.now())
        .thenReturn(now, now) // 1回目, 2回目
        .thenThrow(new RuntimeException()) // 3回目
        .thenReturn(now) // 4回目
        .thenThrow(new RuntimeException()); // 5回目

    // THEN
    // 1回目
    assertThat(offsetDateTimeResolver.now()).isEqualTo(now);
    // 2回目
    assertThat(offsetDateTimeResolver.now()).isEqualTo(now);
    // 3回目
    assertThatThrownBy(() -> offsetDateTimeResolver.now()).isInstanceOf(RuntimeException.class);
    // 4回目
    assertThat(offsetDateTimeResolver.now()).isEqualTo(now);
    // 5回目
    assertThatThrownBy(() -> offsetDateTimeResolver.now()).isInstanceOf(RuntimeException.class);
  }

備考

Mockito.whenにモック対象のメソッドをパラメータに渡すたびにモック設定がリセットされます。

次のコードの例は正常系の設定をした後に、異常系の設定をし直しているので、1回目から異常系の値を返却してしまっています。

  @Test
  void resetされる() {
    // GIVEN
    OffsetDateTime now = OffsetDateTime.now();
    when(offsetDateTimeResolver.now()).thenReturn(now);
    when(offsetDateTimeResolver.now()).thenThrow(new RuntimeException(ERROR_MESSAGE));

    // THEN
    // 1回目が正常系を返却しない
    assertThatThrownBy(() -> offsetDateTimeResolver.now()).isInstance(RuntimeException.class)
    assertThatThrownBy(() -> offsetDateTimeResolver.now()).isInstance(RuntimeException.class)
  }

なお、あくまでソースコードは例です。

実際、上の書き方だとアサーションルーレットと呼ばれるよろしくない書き方になってしまうので、この記事を参考にしてSoftAssertionsを使用してみませんか?

ソースコード

終わりに

Mockitoにて正常系の後に異常系、異常系の後に正常系を返却できるということを勉強できてよかったです。

実際はここまで複雑なケースは出会わないかもしれないのですが、当処理で解消できそうなツイートがありましたので、出会う確率は0ではないと思います。

この記事を書くきっかけになったツイート

twitter.com

類似記事