RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

JUnit5: テストを仕様書にする方法

普段は楽楽精算開発に関わっているrsrksです。
前回投稿のJUnitネタ

tech-blog.rakus.co.jp

に続き

今回はテストを簡易的な仕様書にする方法としてJUnitの階層化を紹介します。

階層化

開発の中で「このメソッドの仕様ってどうなってたっけ?」と思ったら、まず実装を確認することが多いのではないかと思います。
しかし、実装から仕様を読み取るのはなかなか大変です。
「どのパターンでどのような挙動をするか」を頭の中だけで網羅的に把握するのは困難です。把握するためにはパターンを表にまとめて仕様を整理する必要がありますね。

このパターンのまとめをJUnitでは階層化によって実現できます。
最終的にはテストが簡易的な仕様書になるところまで持っていくことができます。

JUnitでの階層化は @Nested アノテーションを付与するだけで実現できます。

ここでは例として「リクエストで飛んできた名前をバリデートするメソッド」のテストを書きたいとします。

バリデートの仕様は簡単に以下のようにします。

  • エラーの場合
    • 0文字の場合は必須エラーを返す
    • 10文字以上の場合は文字数オーバーエラーを返す
  • エラーにならない場合はnullを返す

実装例 (メソッドの中身は適当です)

// 名前のバリデートを行うメソッドをテスト
public class ValidateNameTest {
    String validateName(String name) {
        if (name.equals("")) {
            return "必須エラー";
        }

        if (name.length() >= 10) {
            return "文字数オーバーエラー";
        }

        return null;
    }

    // 名前が無効な場合
    @Nested
    class NameIsInValid {
        // 名前が0文字の場合は必須エラーを返す
        @Test
        public void nameLengthIs0_returnRequiredError() {
            assertEquals("必須エラー", validateName(""));
        }

        // 名前が10文字の場合は文字数オーバーエラーを返す
        @Test
        public void nameLengthIs10_returnRangeError() {
            assertEquals("文字数オーバーエラー", validateName("1234567890"));
        }
    }

    // 名前が有効な場合
    @Nested
    class NameIsValid {
        // 名前が1文字の場合はnullを返す
        @Test
        public void nameLengthIs1_returnNull() {
            assertEquals(null, validateName("1"));
        }

        // 名前が9文字の場合はnullを返す
        @Test
        public void nameLengthIs9_returnNull() {
            assertEquals(null, validateName("123456789"));
        }
    }
}

今回は階層が一つだけですが、@Nested が付いたクラスの下にさらに @Nested をつければ階層はどんどん深くできます。

これだけでも見やすいですが、テストコードが長くなってくると全てのパターンを見るのは中々一苦労です。
そんな時はテストの実行レポートを出力するのがお勧めです。
IntelliJだとテストを実行した時点で以下のような感じで表示され、一目で挙動を確認できます。

テストへの名前付け

上記でテストを実装しましたが、テストメソッドは全て英語で命名しました。
しかし、英語だと命名が難しい、、もっと分かりやすいテスト名にしたい、、という悩みが発生します。
そんな時は @DisplayName() というアノテーションがあります。

@DisplayName("名前のバリデートを行うメソッドをテスト")
public class ValidateNameTest {
    String validateName(String name) {
        if (name.equals("")) {
            return "必須エラー";
        }

        if (name.length() >= 10) {
            return "文字数オーバーエラー";
        }

        return null;
    }

    @DisplayName("名前が無効な場合")
    @Nested
    class NameIsInValid {
        @DisplayName("名前が0文字の場合は必須エラーを返す")
        @Test
        public void nameLengthIs0_returnRequiredError() {
            assertEquals("必須エラー", validateName(""));
        }

        @DisplayName("名前が10文字の場合は文字数オーバーエラーを返す")
        @Test
        public void nameLengthIs10_returnRangeError() {
            assertEquals("文字数オーバーエラー", validateName("1234567890"));
        }
    }

    @DisplayName("名前が有効な場合")
    @Nested
    class NameIsValid {
        @DisplayName("名前が1文字の場合はnullを返す")
        @Test
        public void nameLengthIs1_returnNull() {
            assertEquals(null, validateName("1"));
        }

        @DisplayName("名前が9文字の場合はnullを返す")
        @Test
        public void nameLengthIs9_returnNull() {
            assertEquals(null, validateName("123456789"));
        }
    }
}

↓実行結果も日本語で表示されます。

まとめ

今回はテストを簡易的な仕様書にする方法としてJUnitの階層化を紹介しました。
テストの階層化自体は知っていたけど、何のために行うのかを意識してなかった方はぜひ「テストの仕様書化」を意識してもらえたらと思います。

参考


エンジニア中途採用サイト
ラクスでは、エンジニア・デザイナーの中途採用を積極的に行っております!
ご興味ありましたら是非ご確認をお願いします。
20210916153018
https://career-recruit.rakus.co.jp/career_engineer/

カジュアル面談お申込みフォーム
どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。
以下フォームよりお申込みください。
rakus.hubspotpagebuilder.com

ラクスDevelopers登録フォーム
20220701175429
https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/

イベント情報
会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください!

◆TECH PLAY
techplay.jp

◆connpass
rakus.connpass.com

Copyright © RAKUS Co., Ltd. All rights reserved.