概要
このドキュメントでは、JUnit5 および Mockito を使用して UserController クラスをテストするコードについて解説する。各アノテーションやメソッドの役割、モックの設定方法について取り上げる。
1. アノテーションの解説
@Test
-
役割:
テストメソッドであることを示す。JUnit5 ではこのアノテーションが付与されたメソッドがテストとして認識される。
@ExtendWith(MockitoExtension.class)
役割:
Mockito
を JUnit5 で使用するための拡張機能を提供する。
これにより、@Mock
や @InjectMocks
アノテーションが有効になる。
@InjectMocks
役割:
テスト対象クラスのインスタンスを作成し、@Mock
で作成されたモックオブジェクトを自動的に注入する。
@Mock
役割:
モックオブジェクトを作成する。 when()
メソッドで動作を定義することで、特定の結果を返すように設定できる。
@DisplayName
役割:
テストケースにわかりやすい名前を付ける。実行結果レポートで表示され、テストの意図が明確になる。日本語の説明も可能である。
2. メソッド解説
when()...thenReturn()
-
役割:
モックオブジェクトの特定のメソッドが呼ばれたときに、指定した値を返すように設定する。 -
例:
when(userService.findByUserId(TEST_USER_ID)).thenReturn(Optional.empty());
-
解説:
userService.findByUserId(TEST_USER_ID) が呼ばれると、常に Optional.empty() が返る。これにより「ユーザーが見つからない」ケースをシミュレートする。
anyInt()
-
役割:
任意の整数値が引数として渡された場合でも、モックが反応するように設定する。 -
例:
when(userService.findByUserId(anyInt())).thenReturn(userEntity);
-
解説:
userService.findByUserId() が任意の整数で呼び出されても、userEntity を返す。
verify()
-
役割:
テスト対象メソッドが特定のメソッドを呼び出したかを検証する。 -
例:
verify(redirectAttributes).addFlashAttribute("flashError", "指定されたユーザーが見つかりません。");
-
解説:
テスト対象メソッドが addFlashAttribute() を呼び出したかを確認する。
verifyNoInteractions()
-
役割:
モックオブジェクトに対して、一切のメソッドが呼び出されていないことを検証する。 -
例:
verifyNoInteractions(model);
-
解説:
modelオブジェクトに対して一切の操作が行われていないことを確認する。
サンプルコード
/**
* UserControllerのテストクラス
*/
@ExtendWith(MockitoExtension.class)
public class UserControllerTest {
@InjectMocks
private UserController controller;
@Mock
private UserService userService;
@Mock
private RedirectAttributes redirectAttributes;
@Mock
private HttpSession session;
@Mock
private Model model;
private static final String SHOW_TEMPLATE_PATH = "user/show";
private static final String REDIRECT_INDEX_PATH = "redirect:/user/index";
private static final int TEST_USER_ID = 1;
private static final Integer RESTORE_SESSION_REQUESTED = 1;
private static final ControllerTestHelper controllerTestHelper = new ControllerTestHelper();
/**
* ユーザーが見つからない場合のテストケース.
*/
@DisplayName("ユーザーが見つからない場合のテストケース.")
@Test
public void testShow_UserNotFound() {
// 準備
when(session.getAttribute("logined_user_name")).thenReturn("テストユーザー");
when(userService.findByUserId(TEST_USER_ID)).thenReturn(Optional.empty());
// 実行
String result = controller.show(TEST_USER_ID, model, session, redirectAttributes);
// 検証
assertEquals(REDIRECT_INDEX_PATH, result);
verify(redirectAttributes).addFlashAttribute("flashError", "指定されたユーザーが見つかりません。");
verify(redirectAttributes).addAttribute("restoreSession", RESTORE_SESSION_REQUESTED);
verifyNoInteractions(model);
}
/**
* ユーザーが見つかり、レコードが存在しない場合のテストケース.
* @throws ParseException 日付の形式が間違っていた際に投げられる例外.
*/
@DisplayName("ユーザーが見つかり、レコードが存在しない場合のテストケース.")
@Test
public void testShow_WithoutRecords() throws ParseException {
// 準備
List<RecordListDto> emptyRecordList = new ArrayList<>();
val userEntity = Optional.ofNullable(controllerTestHelper.createTestUserEntity());
when(userService.findByUserId(anyInt())).thenReturn(userEntity);
when(userService.findResultByUserId(anyInt())).thenReturn(emptyRecordList);
// 実行
String result = controller.show(TEST_USER_ID, model, session, redirectAttributes);
// 検証
assertEquals(SHOW_TEMPLATE_PATH, result);
verify(model).addAttribute("user", userEntity.get());
verify(model).addAttribute("hasRecordList", false);
}
/**
* ユーザーが見つかり、レコードが存在する場合のテストケース.
* @throws ParseException 日付の形式が間違っていた際に投げられる例外.
*/
@DisplayName("ユーザーが見つかり、レコードが存在する場合のテストケース.")
@Test
public void testShow_WithRecords() throws ParseException {
// 準備
val recordList = controllerTestHelper.createTestRecordList();
val userEntity = Optional.ofNullable(controllerTestHelper.createTestUserEntity());
when(userService.findByUserId(anyInt())).thenReturn(userEntity);
when(userService.findResultByUserId(anyInt())).thenReturn(recordList);
// 実行
String result = controller.show(TEST_USER_ID, model, session, redirectAttributes);
// 検証
assertEquals(SHOW_TEMPLATE_PATH, result);
verify(model).addAttribute("user", userEntity.get());
verify(model).addAttribute("hasRecordList", true);
}
}