ヘッダー画像

【Java】Mockitoでインスタンスメソッドをモックする方法

投稿 2026年4月18日 最終更新 2026年4月18日 専門用語多め

前置き

レガシーコードを触る機会がありリファクタリングしたいのはやまやまですが、リターンよりリスクのほうが高すぎるので、まずはテストコードを書こうと思いました。

内容的には前回のstaticメソッドのモックの続きで、今回はメソッド内でインスタンス生成をしているクラスのメソッドをモックします。

前提

依存関係

Java17でJUnit5とMockitoを使います。

dependencies {
  implementation(platform("org.junit:junit-bom:5.10.2"))
  testImplementation("org.junit.jupiter:junit-jupiter")
  testImplementation("org.mockito:mockito-junit-jupiter:5.11.0")
  testImplementation("org.mockito:mockito-inline:5.2.0")
}

パターン

今回は下記4パターンのメソッドをモックします。

クラス名 メソッド名 引数 戻り値
LegacyRepository noArgsVoid - -
LegacyRepository argsVoid -
LegacyRepository noArgsReturn -
LegacyRepository argsReturn

テスト対象クラス

クラスのインスタンスを生成して、メソッドを呼び出すテスト対象のメソッドです。

public class LegacyService {

  public void noArgsVoid() {
    LegacyRepository repository = new LegacyRepository();
    repository.noArgsVoid();
  }

  public void argsVoid() {
    LegacyRepository repository = new LegacyRepository();
    repository.argsVoid("123", "test");
  }

  public String noArgsReturn() {
    LegacyRepository repository = new LegacyRepository();
    return repository.noArgsReturn();
  }

  public String argsReturn() {
    LegacyRepository repository = new LegacyRepository();
    return repository.argsReturn("123", "test");
  }

}

モックするインスタンスメソッド

今回モックするインスタンスメソッドです。

実際に実行してしまうと例外が出る体で、実行時エラーを投げます。

これによりモックされており、中身が実行されていないかがわかります。

public class LegacyRepository {

  public static void noArgsVoid() {
    throw new RuntimeException();
  }

  public static void argsVoid(String id, String name) {
    throw new RuntimeException();
  }

  public static String noArgsReturn() {
    throw new RuntimeException();
  }

  public static String argsReturn(String id, String name) {
    throw new RuntimeException();
  }

}

テストコード

これから紹介するテストコードは、下記クラスに記載していきます。

@ExtendWith(MockitoExtension.class)
class LegacyServiceTest {
  @InjectMocks
  private LegacyService legacyService;
  // テストコードを記載
}

引数なし戻り値なしインスタンスメソッドをモック

@Test
void モックしないと実体が実行され例外が投げられる() {
  // when&then
  assertThrows(RuntimeException.class, () -> legacyService.noArgsVoid());
}
@Test
void mockConstructionを行うだけで実体は実行されない() {
  try (MockedConstruction<LegacyRepository> mockLegacyRepository = mockConstruction(LegacyRepository.class)) {
    // when&then
    assertDoesNotThrow(() -> legacyService.noArgsVoid());
  }
}
@Test
void 何もしないことを明示的にモック() {
  try (MockedConstruction<LegacyRepository> mockLegacyRepository = mockConstruction(LegacyRepository.class,
      (mock, context) -> {
        // given
        doNothing().when(mock).noArgsVoid();
      })) {
    // when&then
    assertDoesNotThrow(() -> legacyService.noArgsVoid());
  }
}

引数あり戻り値なしインスタンスメソッドをモック

@Test
void モックしないと実体が実行され例外が投げられる() {
  // when&then
  assertThrows(RuntimeException.class, () -> legacyService.argsVoid());
}
@Test
void mockConstructionを行うだけで実体は実行されない() {
  try (MockedConstruction<LegacyRepository> mockLegacyRepository = mockConstruction(LegacyRepository.class)) {
    // when&then
    assertDoesNotThrow(() -> legacyService.argsVoid());
  }
}
@Test
void 何もしないことを明示的にモック() {
  try (MockedConstruction<LegacyRepository> mockLegacyRepository = mockConstruction(LegacyRepository.class,
      (mock, context) -> {
        // given
        doNothing().when(mock).argsVoid(anyString(), anyString());
      })) {
    // when&then
    assertDoesNotThrow(() -> legacyService.argsVoid());
  }
}

引数なし戻り値ありインスタンスメソッドをモック

@Test
void モックしないと実体が実行され例外が投げられる() {
  // when&then
  assertThrows(RuntimeException.class, () -> legacyService.noArgsReturn());
}
@Test
void mockConstructionを行うだけで実体は実行されない() {
  try (MockedConstruction<LegacyRepository> mockLegacyRepository = mockConstruction(LegacyRepository.class)) {
    // when
    String result = legacyService.noArgsReturn();
    // then
    assertNull(result);
  }
}
@Test
void 戻り値を明示的にモック() {
  try (MockedConstruction<LegacyRepository> mockLegacyRepository = mockConstruction(LegacyRepository.class,
      (mock, context) -> {
        // given
        when(mock.noArgsReturn()).thenReturn("success");
      })) {
    // when
    String result = legacyService.noArgsReturn();
    // then
    assertEquals("success", result);
  }
}

引数あり戻り値ありインスタンスメソッドをモック

@Test
void モックしないと実体が実行され例外が投げられる() {
  // when&then
  assertThrows(RuntimeException.class, () -> legacyService.argsReturn());
}
@Test
void mockConstructionを行うだけで実体は実行されない() {
  try (MockedConstruction<LegacyRepository> mockLegacyRepository = mockConstruction(LegacyRepository.class)) {
    // when
    String result = legacyService.argsReturn();
    // then
    assertNull(result);
  }
}
@Test
void 戻り値を明示的にモック() {
  try (MockedConstruction<LegacyRepository> mockLegacyRepository = mockConstruction(LegacyRepository.class,
      (mock, context) -> {
        // given
        when(mock.argsReturn(anyString(), anyString())).thenReturn("success");
      })) {
    // when
    String result = legacyService.argsReturn();
    // then
    assertEquals("success", result);
  }
}

まとめ

レガシーコードのリファクタリングやテストコードは骨が折れます…。

でも手を動かさないことには一向に状況は変わらないので、頑張ってテストコード書いていこうと思います。

本音ではちょっとあきらめてましたが、Mockitoのおかげで無事テストが書けて良かったです。

以上、ここまで見ていただきありがとうございます。

皆さまの快適な開発ライフに、ほんの少しでもお役に立てれば幸いです。

コメント

この記事のコメントはありません。

TOP