java-recipes

ホーム テスト(Testing) › Test-01: JUnit 5 基本

Test-01: JUnit 5 基本(@Test, @BeforeEach, @AfterEach)

JUnit 5(JUnit Jupiter)はJava の標準的なテストフレームワークです。@Test でテストメソッドを宣言し、@BeforeEach で各テスト前の初期化、@AfterEach で各テスト後のクリーンアップを行います。 JUnit 5 は Java 8 以上で利用できます。

JUnit 5 の主要アノテーション

JUnit 5 では、アノテーションを使ってテストの構造を定義します。 JUnit 4 と比べてアノテーション名が変わっている点に注意してください(例:@Before@BeforeEach)。

アノテーションタイミング用途
@Testテストメソッドに付与このメソッドがテストであることを宣言
@BeforeEach各テストメソッドの前Calculator のインスタンス生成など初期化処理
@AfterEach各テストメソッドの後ファイルクローズ・null 代入などクリーンアップ
@BeforeAllクラス内の全テスト実行前(1回のみ)DB 接続など高コストな初期化(static メソッドが必要)
@AfterAllクラス内の全テスト実行後(1回のみ)DB 切断など高コストなクリーンアップ(static メソッドが必要)
@DisplayNameテストメソッド・クラスに付与テスト結果に表示する日本語の名前
@Nested内部クラスに付与テストをグループ化して構造化
@ParameterizedTestパラメータ化テストに付与@CsvSource / @ValueSource などと組み合わせて複数の値でテスト
@TestFactory動的テスト生成メソッドに付与Stream<DynamicTest> を返してテストを動的に生成

主要なアサーションメソッド(Assertions クラス)

メソッド検証内容
assertEquals(expected, actual)期待値と実際値が等しいか
assertNotEquals(unexpected, actual)値が等しくないか
assertTrue(condition)条件が true か
assertFalse(condition)条件が false か
assertNull(object)null か
assertNotNull(object)null でないか
assertThrows(ExceptionClass, () -> ...)指定した例外が発生するか
assertAll("msg", () -> ..., () -> ...)複数のアサーションをまとめて検証(全部失敗を報告)

サンプルコード

テスト対象クラス(Calculator)と、 JUnit 5 でのテストクラス(CalculatorTest)をコメントで示しています。 Java 8 版では基本的な @Test@ParameterizedTest を紹介します。 Java 17 版では @Nested でテストをグループ化します。 Java 21 版では @TestFactory で動的テストを生成します。

JUnit5BasicSample.java
// Maven 依存定義(pom.xml に追加)
// <dependency>
//     <groupId>org.junit.jupiter</groupId>
//     <artifactId>junit-jupiter</artifactId>
//     <version>5.10.1</version>
//     <scope>test</scope>
// </dependency>

public class JUnit5BasicSample {

    // テスト対象クラス(実際は本番コード)
    static class Calculator {
        int add(int a, int b) { return a + b; }
        int subtract(int a, int b) { return a - b; }
        int multiply(int a, int b) { return a * b; }
        int divide(int a, int b) {
            if (b == 0) {
                throw new ArithmeticException("0除算はできません");
            }
            return a / b;
        }
    }

    /*
     * JUnit 5 テストクラスのサンプル(src/test/java/CalculatorTest.java)
     *
     * import org.junit.jupiter.api.*;
     * import org.junit.jupiter.params.ParameterizedTest;
     * import org.junit.jupiter.params.provider.CsvSource;
     * import static org.junit.jupiter.api.Assertions.*;
     *
     * class CalculatorTest {
     *
     *     private Calculator calc;
     *
     *     @BeforeEach  // 各テストメソッドの前に実行
     *     void setUp() {
     *         calc = new Calculator();
     *     }
     *
     *     @Test
     *     @DisplayName("正の数の加算が正しく計算される")
     *     void testAddPositive() {
     *         // Given-When-Then パターン
     *         int result = calc.add(3, 5);
     *         assertEquals(8, result, "3 + 5 は 8 のはず");
     *     }
     *
     *     @Test
     *     @DisplayName("ゼロ除算で ArithmeticException が発生する")
     *     void testDivideByZero() {
     *         assertThrows(ArithmeticException.class,
     *             () -> calc.divide(10, 0),
     *             "0除算は例外を投げるはず");
     *     }
     *
     *     @ParameterizedTest
     *     @CsvSource({ "1,2,3", "0,0,0", "-1,1,0", "100,200,300" })
     *     @DisplayName("様々な値の加算テスト")
     *     void testAddParameterized(int a, int b, int expected) {
     *         assertEquals(expected, calc.add(a, b));
     *     }
     *
     *     @Test
     *     @DisplayName("複数のアサーションをまとめて検証")
     *     void testMultipleAssertions() {
     *         assertAll("加算と減算の検証",
     *             () -> assertEquals(8,  calc.add(3, 5)),
     *             () -> assertEquals(-2, calc.subtract(3, 5)),
     *             () -> assertEquals(15, calc.multiply(3, 5))
     *         );
     *     }
     *
     *     @AfterEach  // 各テストメソッドの後に実行
     *     void tearDown() {
     *         // テスト後のクリーンアップ(必要な場合)
     *         calc = null;
     *     }
     * }
     */

    public static void main(String[] args) {
        Calculator calc = new Calculator();
        System.out.println("=== Calculator のテスト(JUnit 5 サンプル) ===");
        System.out.println("add(3, 5) = " + calc.add(3, 5));
        System.out.println("subtract(10, 3) = " + calc.subtract(10, 3));
        System.out.println("multiply(4, 5) = " + calc.multiply(4, 5));
        System.out.println("divide(10, 2) = " + calc.divide(10, 2));
        try {
            calc.divide(10, 0);
        } catch (ArithmeticException e) {
            System.out.println("divide(10, 0) → 例外: " + e.getMessage());
        }
        System.out.println("\n上記コードのテストは CalculatorTest.java(JUnit 5)で確認してください");
    }
}

よくあるミス・注意点

JUnit 4 と JUnit 5 のアノテーションを混在させない

JUnit 4 の @Before と JUnit 5 の @BeforeEach は別物です。 パッケージも異なります(org.junit vsorg.junit.jupiter.api)。 混在すると一方のアノテーションが無視されてテストが期待通りに動きません。 インポートを必ず確認してください。

@BeforeAll / @AfterAll は static メソッドに付与する

@BeforeAll@AfterAll を付けるメソッドはstatic である必要があります(デフォルト)。static でない場合は@TestInstance(Lifecycle.PER_CLASS) を クラスに付けることで解決できます。

assertThrows は例外の型を正確に指定する

assertThrows(Exception.class, ...) のように 親クラスを指定すると、意図しない例外でもテストが通過してしまいます。assertThrows(ArithmeticException.class, ...) のように 具体的な例外クラスを指定してください。

テストする観点

  • 加算・減算・乗算の正常系(正の数・負の数・ゼロ)が正しく計算されること
  • 除算で 0 を渡したとき ArithmeticException が発生すること
  • 境界値テスト: Integer.MAX_VALUEInteger.MIN_VALUE を使った加算でオーバーフローが起きないか(または意図した挙動か)
  • @BeforeEach で初期化した Calculator が各テストで独立していること(テスト間の状態漏れがないか)
  • @ParameterizedTest で複数の入力値(正常値・境界値・異常値)を網羅していること

GitHub でソースコードを見る →