きり丸の技術日記

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

JavaのMockitoで部分モックをしたい・本物のメソッドを呼びたい(PartialMock, Answer)

JavaのMockitoを使って、対象クラスをモックにすることがあります。ただし、該当クラスのメソッドすべてではなく一部だけモックにして、他は本物のメソッドを呼びたいことがあります。

今回は、モッククラスから本当のメソッドを呼ぶ方法、デフォルト設定で本当のメソッドを呼ぶモックにする方法を記載します。

なお、このように部分的にモックすることを部分モック、Partial Mockと呼びますので、以後はそのように記載します。

環境

  • Java
    • 17
  • org.mockito:mockito-core
    • 4.5.1

ユースケース

  • クラス全体ではなく、一部だけ本物のメソッドを呼びたい

私は、日付Utilクラスでよくこのユースケースが発生します。現在時刻の取得メソッドの戻り値は固定にしたいが、同クラスの現在時刻を目的のフォーマットの文字列で取得するメソッドは本物のメソッドを呼びたい、等々があります。

前提

Mockitowhenは特に解説しません。

動作確認用として、メソッドを2つ用意し、1つは片方に依存しているメソッドを用意します。呼び出順番を意識したgetFirst, getSecondと命名しています。

public class Target {
  public String getFirst() {
    return "1";
  }

  public String getSecond() {
    return getFirst();
  }
}

対応

本物のメソッドを呼びたい

MockitoのthenCallRealMethodを呼び出します。

@Test
void test_01() {
  Target target = Mockito.mock(Target.class);
  
  // モックにしただけなので、nullが返却される
  assertThat(target.getFirst()).isNull();
  assertThat(target.getSecond()).isNull();

  // getFirstをモックで固定したが、getSecondはモックのまま
  when(target.getFirst()).thenReturn("123");
  assertThat(target.getFirst()).isEqualTo("123");
  assertThat(target.getSecond()).isNull();

  // getSecondは本物メソッドを呼び出したため、getSecondも値を返却している
  when(target.getSecond()).thenCallRealMethod();
  assertThat(target.getFirst()).isEqualTo("123");
  assertThat(target.getSecond()).isEqualTo("123");
}

基本は本物メソッドを呼ぶモックにする

thenCallRealMethodはメソッドごとに設定する必要がありますので、非常に面倒です。

ネストが深かったり、本物を呼び出したいメソッドの量が多い場合は、モックのタイミングで設定しましょう。第2引数にMockito.CALLS_REAL_METHODを渡しましょう。

@Test
void test_01() {
  Target target = Mockito.mock(Target.class, Mockito.CALLS_REAL_METHODS);
  // デフォルトの"1"を返却する
  assertThat(target.getFirst()).isEqualTo("1");
  assertThat(target.getSecond()).isEqualTo("1");
  
  // getFIrstを"123"に固定したので、依存するgetSecondも"123"を返却します。
  when(target.getFirst()).thenReturn("123");
  assertThat(target.getFirst()).isEqualTo("123");
  assertThat(target.getSecond()).isEqualTo("123");
}

@Mock, @MockBean等々のアノテーションでモックにする場合も、パラメータで制御できます。

@Mock(answer = Answers.CALLS_REAL_METHODS)
Target target;

@MockBean(answer = Answers.CALLS_REAL_METHODS)
Target target;

ソースコード

終わりに

正直なところ、類似記事でも同様の紹介自体はしているので、そちらを読めば理解や推測もできる記事ではあります。

ただ、部分モックのやり方、本物メソッドの呼び方等々に着目したタイトルではないため、あらためて記事にしました。前回書いていなかったアノテーションでのモック方法等々も追記したので、少しだけ進歩があると自分の中で信じることにします。

類似情報