《あなたが知らないJAVA》ユニットテストの命名哲学
ユニットテストの設計哲学
プログラムはまず人間が読むために書かれ、その後に機械が実行できるようにする。
Programs must be written for people to read, and only incidentally for machines to execute.
そして、プログラミングの第一歩は命名であり、ユニットテストも同様です。
WWW
ユニットテストの命名をどのように人間が理解できるようにするか?命名において3つの要素を反映する必要があります。略してWWW原則と呼びます。
- 何をテストするか?(what)
- どのような条件下でテストするか?(when)
- どのような結果を期待するか?(want)
以下に、MJGAスキャフォールドのユニットテスト例を示します。
@Test
void createVerifyGetSubjectJwt_givenUserIdentify_shouldReturnTrueAndGetExpectIdentify() {
String jwt = cookieJwt.createJwt("1");
assertThat(cookieJwt.verifyToken(jwt)).isTrue();
assertThat(cookieJwt.getSubject(jwt)).isEqualTo("1");
}
この命名を3つの部分に分けると以下のようになります:
-
createVerifyGetSubjectJwt
はテスト対象を示しています。 -
givenUserIdentify
はテストの条件を示しています。 -
shouldReturnTrueAndGetExpectIdentify
は期待される動作を説明しています。
これが良い命名です——ビジネス担当者でも理解できる命名です。
AAA
第一関門は終わりました。次にテストロジックを実装します。実はWWWの命名設計があれば、ロジックの実装にはすでに根拠があります。命名と同様に、テストロジックにも参考にできる原則があり、通常AAA原則と呼ばれます。
- 準備(Arrange)
- 実行(Act)
- 断言(Assert)
MJGAスキャフォールドのテストケースを見てみましょう:
@Test
@WithMockUser
void signIn_givenValidHttpRequest_shouldSucceedWith200() throws Exception {
String stubUsername = "test_04cb017e1fe6";
String stubPassword = "test_567472858b8c";
SignInDto signInDto = new SignInDto();
signInDto.setUsername(stubUsername);
signInDto.setPassword(stubPassword);
when(signService.signIn(signInDto)).thenReturn(1L);
mockMvc
.perform(
post("/auth/sign-in")
.contentType(MediaType.APPLICATION_JSON)
.content(
"""
{
"username": "test_04cb017e1fe6",
"password": "test_567472858b8c"
}
""")
.with(csrf()))
.andExpect(status().isOk());
}
Arrange
これはテストの準備段階です。この段階では、コンテキストを提供するためのコードを構築する必要があります。例えば、データの挿入、オブジェクトの構築、さらにはより複雑なmockやstubもこれに含まれます。例では、SignInDtoオブジェクトを構築し、後続の使用に備え、signInメソッドの戻り値を仮定しています——これらはすべて後続のテストの準備です。
Act
これは実行段階で、例では mockMvc.perfom
に対応します。ここではApiが複雑ですが、1行のコードと理解してください。
Assert
期待する結果を明確に示す必要があります。例では .andExpect(status().isOk());
に対応します。
多くの場合、特定のロジックがエラーを発生させずに正常に実行できるかどうかを検証するためにテストメソッドを書く必要があります。これは、メソッド自体に戻り値がないためです。これは簡単で、 assertDoesNotThrow
を使用してこの問題を解決します。
// pause & resume job
JobKey firstDataBackupJobKey = dataBackupJobKeys.iterator().next();
assertDoesNotThrow(
() -> {
dataBackupScheduler.pauseJob(firstDataBackupJobKey);
dataBackupScheduler.resumeJob(firstDataBackupJobKey);
});
これらのコーディング哲学は、使用する言語やフレームワークに関係なく、すべてのユニットテストはこのような考え方で書かれています。より多くのユニットテストの内容については、私が後で整理して公開します。
最後に
- 私はChuck1snです。現代のJvmエコシステムの普及に長年取り組んでいる開発者です。
- あなたの返信、いいね、コレクションは、私が更新を続けるための動力です。
- 簡単なワンクリックの三連は、私にとって大きなサポートであり、非常に感謝しています!
- 私のアカウントをフォローして、記事の更新を最初に受け取ってください。
PS:上記のすべてのコード例は、Githubリポジトリで見つけることができます。役に立った場合は、ぜひStarを付けてください。これは私にとって大きな励みになります。ありがとうございます!
Discussion