JUnit 5のアーキテクチャ、Maven設定、JUnit 4からの移行

最終更新: 2026-08-17 10:18:23 +0800 +0800
tags: Java | Junit | Maven | Testing
原文は英語で書かれています。あなたはAI翻訳版を読んでいます。

JUnit 5は、テストの記述に使用するAPIを、テストを検出して実行する インフラストラクチャから分離しています。この分離により、Maven、IDE、 およびカスタムランチャーは、一つのインターフェイスを通じて複数の テストモデルを実行できます。

例ではJUnit 5.13.4を対象とします。JUnit 6も同じアーキテクチャを維持していますが、 JUnit 5を使い続ける必要があるプロジェクトでは、 バージョン指定されたJUnit 5ドキュメント を使用してください。

Platform、Jupiter、Vintage #

JUnit 5は三つのコンポーネントで構成されます。

コンポーネント責務
JUnit Platformテストを検出し、登録されたテストエンジンを起動します。Maven SurefireとIDEはこのレベルで統合されます。
JUnit JupiterJUnit 5のプログラミングモデル、拡張API、およびテストエンジンを定義します。
JUnit Vintage移行期間中にJUnit 3およびJUnit 4のテストをPlatform上で実行します。

Jupiterテストは、@Test とアサーションを org.junit.jupiter.api から インポートします。SurefireはPlatformにテストの検出を要求し、その後 junit-jupiter-engine が検出されたメソッドを実行します。テスト依存関係には Mavenの test スコープを使用するため、アプリケーションコードはいずれの JUnitコンポーネントにも依存しません。

最小構成のMavenプロジェクト #

次のレイアウトでは、プロダクション用クラスとテストクラスを分離します。

text
junit-hello/
├── pom.xml
└── src
    ├── main/java/com/example/Greeter.java
    └── test/java/com/example/GreeterTest.java

保守可能な最小構成のPOMには org.junit.jupiter:junit-jupiter を使用します。 このアーティファクトは、Jupiter API、パラメーター化テストのサポート、 およびランタイムエンジンを集約しています。

xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>junit-hello</artifactId>
  <version>1.0-SNAPSHOT</version>

  <properties>
    <maven.compiler.release>17</maven.compiler.release>
    <junit.version>5.13.4</junit.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>3.5.4</version>
      </plugin>
    </plugins>
  </build>
</project>
java
package com.example;

public final class Greeter {
    public String greet(String name) {
        if (name == null || name.isBlank()) {
            throw new IllegalArgumentException("name must not be blank");
        }
        return "Hello, " + name;
    }
}
java
package com.example;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import org.junit.jupiter.api.Test;

class GreeterTest {
    private final Greeter greeter = new Greeter();

    @Test
    void greetsByName() {
        assertEquals("Hello, Ada", greeter.greet("Ada"));
    }

    @Test
    void rejectsBlankNames() {
        var error = assertThrows(
                IllegalArgumentException.class,
                () -> greeter.greet(" "));

        assertEquals("name must not be blank", error.getMessage());
    }
}

Mavenを通じてテストを実行します。

sh
mvn test

Jupiterのテストクラスとメソッドには、package-private可視性を指定できます。 テストメソッドを private または static にすることはできませんが、 ライフサイクルと拡張の規則では、特定の場所で static が許可されます。

JUnitアーティファクトの選択 #

機能が必要な場合に限り、別のアーティファクトを追加します。

アーティファクト追加する場合
junit-jupiterアプリケーションのテストスイートでは集約依存関係を優先します。API、パラメーター、およびエンジンがすでに含まれます。
junit-jupiter-apiランタイムエンジンを別に管理しながら、APIに対してテストまたは拡張をコンパイルする場合。
junit-jupiter-enginejunit-jupiter の代わりに細分化した依存関係を使用してJupiterテストを実行する場合。テストランタイムスコープを使用します。
junit-jupiter-params細分化した設定に @ParameterizedTest のサポートを追加する場合。集約依存関係にはすでに含まれています。
junit-vintage-engine変更していないJUnit 3またはJUnit 4のテストをPlatform経由で実行する場合。
junit-jupiter-migrationsupportテストをJupiterへ変換する間、一部のJUnit 4ルールと @Ignore を一時的に適応させる場合。
junit-platform-suitePlatformのスイートアノテーションを使用してスイートを宣言する場合。
junit-platform-launcherプログラムからテストを検出して実行する場合、またはテストツールを実装する場合。アプリケーションのテストスイートが直接必要とするものではありません。
junit-platform-console-standaloneMavenまたはIDEの外部で、一つの実行可能JARからテストを実行する場合。このJARはプロジェクトのPOMに含めません。

ビルドで複数のJUnitアーティファクトを宣言する場合は、JUnit BOMを インポートしてください。BOMによってPlatform、Jupiter、およびVintageの バージョンの互換性が保たれるためです。

xml
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.junit</groupId>
      <artifactId>junit-bom</artifactId>
      <version>${junit.version}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

BOMの対象となるアーティファクト宣言では、<version> を省略できます。

Surefire 3.0より前のリリースでは、明示的な junit-platform-launcher 依存関係が 必要でした。現在のSurefire 3.xリリースでは、テストランタイム・クラスパス上の ランチャーがPlatformと整合するため、通常のテストスイートにランチャーを 追加してもテスト機能は増えず、管理対象のバージョンが増えるだけです。

Jupiterプログラミングモデル #

Jupiterは、一般的なテスト構造に特化したアノテーションを提供します。

  • @BeforeEach と @AfterEach はテストごとのフィクスチャを構築します。
  • @BeforeAll と @AfterAll はクラスレベルのフィクスチャを構築します。
  • @ParameterizedTest は、@ValueSource、@CsvSource、@MethodSource、 または別の引数ソースから得たデータを使い、一つのテスト本体を実行します。
  • @Nested は、ライフサイクルの意味を維持しながら関連するケースをまとめます。
  • @Tag は、ビルド時に含める、または除外するテストを分類します。
  • @ExtendWith は、パラメーター解決、コールバック、条件付き実行、および その他の横断的な振る舞いに対応する拡張を登録します。

テストでは、実行順序ではなく観測可能な結果をアサートすべきです。 Jupiterはメソッド名から自明に定まる順序を保証せず、順序付きテストは 状態を共有するため、失敗が前のケースに依存します。

JUnit 4テストを変更せずに実行する #

JupiterとJUnit 4は異なるJavaパッケージを使用し、一つのモジュール内で 共存できるため、Vintageの導入は最もリスクの低い最初の移行手順です。

xml
<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.13.2</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.junit.vintage</groupId>
  <artifactId>junit-vintage-engine</artifactId>
  <version>${junit.version}</version>
  <scope>test</scope>
</dependency>

これにより、SurefireはPlatformを通じてJupiterとJUnit 4のテストを検出します。 Vintageによる実行が成功してもJupiterのプログラミングモデルや拡張モデルは 使用されないため、古いテストが残っている間だけVintageを維持してください。

JUnit 4テストの変換 #

一般的なソースコード上の対応関係は次のとおりです。

JUnit 4Jupiter
@Before / @After@BeforeEach / @AfterEach
@BeforeClass / @AfterClass@BeforeAll / @AfterAll
@Ignore@Disabled
@Category@Tag
@RunWith一つ以上の @ExtendWith 拡張
@Rule / @ClassRule拡張またはJupiterネイティブのフィクスチャ
@Test(expected = E.class)assertThrows(E.class, executable)

@RunWith から @ExtendWith への移行は、機械的な名前変更ではなく、 概念上の対応付けです。JUnit 4のクラスは一つのrunnerしか持てませんが、 Jupiterは複数の拡張を組み合わせられるためです。

JUnit 4では、任意のアサーションメッセージを先頭に置きます。

java
assertEquals("unexpected greeting", expected, actual);

Jupiterでは、メッセージを最後に置きます。

java
assertEquals(expected, actual, "unexpected greeting");

アノテーションとライフサイクルコードをJupiterへ移行する間も、既存の org.junit.Assert 呼び出しは残せます。アサーションを別に変換すれば、 各移行変更の規模を小さくできます。

ルールは個別に扱う必要があります。junit-jupiter-migrationsupport アーティファクトは、ExternalResource、Verifier、ExpectedException の ルールファミリーを適応させます。@EnableRuleMigrationSupport は、それらの アダプターを有効にします。@EnableJUnit4MigrationSupport はJUnit 4の @Ignore も認識します。その他のカスタムルールには、Jupiter拡張または フィクスチャの直接的な書き換えが必要です。

したがって、管理された移行は次の三段階で進めます。

  1. Vintageを追加し、Platformベースのテストコマンドを一つ確立する。
  2. 名前ではなく振る舞いに基づいてrunnerとルールを置き換えながら、 クラスをJupiterへ変換する。
  3. ビルド内にJUnit 3またはJUnit 4のテストがなくなった後、Vintage、JUnit 4、 および移行サポートを削除する。

参考文献 #

コメント

GitHub で閲覧・コメントする(投稿にはログインが必要です)