本ページはプロモーションが含まれています

Java入門

JUnit5の使い方入門|書き方を基礎から解説【2026年】

JUnit 5(テストの書き方入門)

トム

SESで約8年、環境を変えられないもどかしさの中にいました。32歳で自社開発企業に転職し、今はClaude Codeと自動化で、仕事も時間も組み直している途中です。
・Javaバックエンドエンジニア(Java歴12年以上)
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎
詳細なプロフィール

「動くことは動く。でも、直したところが他を壊していないか、実は自信がない」。そう感じたことがあるなら、原因はテストがないことだ。

JUnit5を使えば、@Testと数行のアサーションだけで、この不安を毎回機械的に確認できるようになる。

JUnit5の使い方は、(1) Maven/Gradleに依存関係を追加する、(2) src/test/javaに@Test付きのテストクラスを書く、(3) mvn testかIDEで実行して結果を確認する、の3ステップだ。

本記事では3ステップを順に解説したうえで、例外テスト・Arrange-Act-Assertによるテスト設計・Mockitoを使ったモック化まで、実務でそのまま使える形で扱う。

JUnit4からの変更点も押さえているので、既存プロジェクトでの移行判断にも使える。

この記事でわかること

  • JUnit5の導入方法(Maven/Gradle)
  • @Test・アサーション・例外テストの基本的な書き方
  • mvn test・gradle test・IDEでのテスト実行方法
  • Arrange-Act-Assertパターンによるテスト設計
  • @BeforeEach・@ParameterizedTest・@Nestedなどの便利機能
  • Mockitoを使ったモックテストの基礎

JUnit5の書き方早見表(コピペ用)

テストを書くたびに「あのアノテーション、何だっけ」となるものを1つの表にまとめた。ブックマークしておけば、次からはこの表だけで足りる。

やりたいこと書き方
アサーションをまとめて使えるようにするimport static org.junit.jupiter.api.Assertions.*;
テストメソッドを作る@Test
値が等しいか確かめるassertEquals(期待値, 実際の値, "失敗時のメッセージ")
条件がtrueか確かめるassertTrue(条件)
例外が出るか確かめるassertThrows(IllegalArgumentException.class, () -> 処理)
各テストの前後に処理を入れる@BeforeEach / @AfterEach
複数の値で同じテストを回す@ParameterizedTest + @ValueSource(strings = {"", " "})
テスト名を日本語で表示する@DisplayName("2 + 3 = 5 になること")
テストをグループに分ける@Nested(内部クラスに付ける)
テストを一時的に止める@Disabled("理由")
依存オブジェクトをモックにする@ExtendWith(MockitoExtension.class) + @Mock / @InjectMocks
すべてのテストを実行mvn test / ./gradlew test
1クラスだけ実行mvn test -Dtest=CalculatorTest / ./gradlew test --tests CalculatorTest

assertEquals 以外のアサーション(assertAll・assertTimeout など)は、JUnit assertメソッド一覧の早見表にまとめてある。

JUnit5とは?JUnit4との違いも解説

JUnitは、Javaのユニットテスト(単体テスト)を書いて実行するためのフレームワークです。Javaのテストツールとして最も広く使われています。

JUnitは高品質なソフトウェア開発に欠かせません。 コードが意図通りに動くかを自動で検証でき、バグの早期発見やリファクタリング(コードの改善作業)を支援してくれるからです。

ユニットテストの目的と重要性

ユニットテストの目的は、クラスやメソッドといったプログラムの最小単位(ユニット)が、それぞれ正しく動くかを確かめることです。

ユニットテストを導入するメリットは主に3つです。

メリット

  1. 品質の向上: バグを開発の早い段階で発見し、修正できます。
  2. リファクタリングの促進: テストコードがあれば、コードを修正した際に意図しない不具合(デグレード)が発生していないかを即座に確認できます。
  3. 仕様のドキュメント化: よく書かれたテストコードは、そのコードが「どのように動くべきか」を示す生きたドキュメントです。

この「仕様のドキュメント化」というメリットは、筆者自身、前任者が退職しドキュメントがほぼゼロの状態でシステムを引き継いだときに実感したことだ。

仕様書はなく、頼れるのはコードだけ。

それでもテストコードが残っていたクラスは、半年かけての引き継ぎ作業の中でも「どう動くべきか」が読むだけでわかり、把握のスピードが明らかに違った。

テストコードがなければ、仕様の把握はもっと長引いていたはずだ。

JUnitの概要とバージョンの違い(JUnit4とJUnit5)

JUnitにはいくつかのバージョンがありますが、現在主流なのはJUnit5です。以前はJUnit4が広く使われていました。

JUnit4とJUnit5では、アーキテクチャとアノテーション名が大きく変わりました。

例えば、テスト前の初期化処理は、JUnit4では@Beforeでしたが、JUnit5では@BeforeEachに変わりました。

JUnit5は、JUnit Platform・JUnit Jupiter・JUnit Vintageの3つのモジュールに分かれました。

テストを動かす土台(Platform)と書き方(Jupiter)が分離されたため、拡張しやすくなっています。

JUnit6は2025年9月30日に正式リリース(GA)されました。以降の修正版は公式のリリースノートで確認できます。

一方のJUnit5も、2026年4月に5.14.4が出るなど保守リリースが続いています。

ただし、Java17以上が必須になったことや既存プロジェクトの移行コストから、実務では引き続きJUnit5(Jupiter)が広く使われています。

これから初めてJavaのテストを学ぶなら、まずはJUnit5から始めるのが現実的です。

JUnit4とJUnit5の主な違いを表にまとめました。

項目JUnit4JUnit5
テストアノテーション@Test(org.junit)@Test(org.junit.jupiter.api)
初期化@Before@BeforeEach
全体初期化@BeforeClass@BeforeAll
無効化@Ignore@Disabled
アーキテクチャ単一jarPlatform + Jupiter + Vintage
Java最低バージョンJava 5Java 8

JUnit4からJUnit5への移行手順

既存プロジェクトでJUnit4からJUnit5に移行する場合、以下の手順で進めます。

  1. 依存関係の変更: pom.xml/build.gradleからjunit:junitを削除し、junit-jupiter-api・junit-jupiter-engineに置き換えます。
  2. 段階的な移行: junit-vintage-engineを依存関係に追加すれば、既存のJUnit4形式のテストを残したまま、新規テストだけJUnit5で書けます。テストを一度に全て書き直す必要はありません。
  3. アノテーション名の一括置換: @Before→@BeforeEach、@BeforeClass→@BeforeAllなど、上記の比較表に沿ってIDEの一括置換機能で変更します。
  4. ランナーの置き換え: Spring Bootプロジェクトなどで@RunWith(SpringRunner.class)を使っている場合は置き換えが必要です。JUnit5では@RunWith自体が廃止されたため、@ExtendWith(SpringExtension.class)を使います。

JUnit5の導入方法(Maven/Gradle)

JUnitをプロジェクトに導入するのは簡単です。

特に、MavenやGradleといったビルドツールを利用すると、数行の設定を追加するだけで済みます。

Maven/Gradleによる導入

最近のJavaプロジェクトでは、依存ライブラリの管理にMavenまたはGradleを使用するのが一般的です。

Mavenの場合 (pom.xml)

pom.xmlファイルの<dependencies>セクションに、以下の依存関係を追加します。

xml
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.14.4</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.14.4</version>
    <scope>test</scope>
</dependency>

<scope>test</scope>は、このライブラリがテストコードのコンパイルと実行時にのみ必要であることを示します。

JUnit5はJava 8以上で動作します。

2026年現在の最新LTSはJava 25(2025年9月リリース)で、Java 21 LTSと並んで実務の主流です。

次期LTSはJava 29(2027年9月予定)です。

Gradleの場合 (build.gradle)

build.gradleファイルに以下を記述します。Gradle 9以降は junit-platform-launcher を宣言しないとテストが起動しないため、この行も忘れずに入れてください。

groovy
dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.14.4'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.14.4'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.14.4'
}

test {
    useJUnitPlatform()
}
あわせて読む

IDE(IntelliJ/Eclipse)でのセットアップ

IntelliJ IDEA・Eclipse・VS Code(Extension Pack for Java)などのIDEを使っている場合、さらに簡単です。

たとえばIntelliJ IDEAでGradleプロジェクトを新規作成すると、通常はJUnitの依存関係が最初から入っています(Mavenプロジェクトや入っていない場合は上記の依存関係を追加します)。

テストクラスを手動で作る場合も、IDEがJUnitの追加を提案してくれます。

JUnit5の基本的なテストの書き方

導入が終わったら、ここからはテストコードを書きます。

ここでは、計算を行うシンプルなCalculatorクラスを例に、基本的なテストの書き方を解説します。

java
public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
}

@Testアノテーションの使い方

JUnit5では、テスト用に書いたメソッドに@Testアノテーションを付けると、JUnitがそのメソッドをテストケースとして実行します。

テストクラスは、慣習としてsrc/test/javaディレクトリ配下に作成します。

java
import org.junit.jupiter.api.Test;

class CalculatorTest {
    @Test
    void testAdd() {
        // ここにテストのロジックを記述する
    }
}

アサーション(assertEquals / assertTrueなど)

テストコードの核心は「アサーション(Assertion)」、つまりメソッドの実行結果が期待どおりかを検証する仕組みです。

JUnit5ではAssertionsクラスのstaticメソッドを使います。

最もよく使われるのが、2つの値が等しいかを確認するassertEqualsです。

java
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class CalculatorTest {
    @Test
    void 足し算が正しくできるか検証する() {
        Calculator calculator = new Calculator();
        int result = calculator.add(2, 3);
        assertEquals(5, result, "2 + 3 は 5 になるべきです");
    }
}

assertEqualsは、第1引数に「期待値」、第2引数に「実際の値」を取ります。

第3引数は、テストが失敗したときに表示されるメッセージで、省略できます。

他にも、以下のような便利なアサーションメソッドがあります。

アサーションメソッド

  • assertTrue(boolean condition): 条件がtrueであることを検証
  • assertFalse(boolean condition): 条件がfalseであることを検証
  • assertNotNull(Object obj): オブジェクトがnullでないことを検証
  • assertNull(Object obj): オブジェクトがnullであることを検証

assertAllやassertIterableEqualsを含む全メソッドの使い分けは、JUnitのassertメソッド一覧(早見表)にまとめています。

例外の検証

特定の条件下で、メソッドが正しく例外をスローするかを検証したいケースもあります。

JUnit5では、assertThrowsメソッドを使って簡単に例外テストを記述できます。

java
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class SomeServiceTest {
    @Test
    void 引数がnullの場合に例外をスローするか() {
        SomeService service = new SomeService();
        // service.process(null) を実行した際に IllegalArgumentException がスローされることを期待する
        assertThrows(IllegalArgumentException.class, () -> {
            service.process(null);
        });
    }
}

assertThrowsの第1引数には期待する例外の型、第2引数には例外が出るはずの処理をラムダ式で渡します。

意図した例外が正しく発生するかを安全にテストできます。

テストの実行方法(Maven/Gradle/IDE)

テストは書いただけでは動きません。ビルドツールのコマンドかIDEから実行して、はじめて結果がわかります。

コマンドラインで実行する場合は、プロジェクトのルートディレクトリで次のように入力します。

bash
# Maven:すべてのテストを実行
mvn test

# Maven:CalculatorTestだけを実行
mvn test -Dtest=CalculatorTest

# Gradle:すべてのテストを実行
./gradlew test

# Gradle:CalculatorTestだけを実行
./gradlew test --tests CalculatorTest

Mavenで実行すると、最後に次のような集計行が表示されます。

text
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS

FailuresとErrorsが0なら全テスト成功です。Failuresはアサーションの不一致、Errorsは想定外の例外、Skippedは@Disabledなどで飛ばしたテストの件数を表します。

「Tests run: 0」と表示されてテストが見つからない場合は、MavenのSurefireプラグインが古い可能性があります。JUnit5のテストを実行するには、maven-surefire-pluginを2.22.0以上(できれば3系)に上げてください。

IDEから実行する場合、IntelliJ IDEAはテストクラスやメソッドの横にある実行ボタン(▶)をクリックします。Eclipseはテストクラスを右クリックし、[実行]→[JUnitテスト]を選びます。

どちらも結果は緑(成功)・赤(失敗)で一覧表示されます。

テストケースの構造と設計

読みやすく保守しやすいテストを書くには、構造と命名が大切です。3つのポイントを紹介します。

Arrange-Act-Assertパターンとは

テストコードはArrange-Act-Assert (AAA) パターンで書くのが効果的です。 テストの目的が明確になり、誰が見てもわかりやすくなります。

AAAパターン

  • Arrange(準備): テスト対象のオブジェクトを生成し、必要なデータや状態を準備するフェーズ。
  • Act(実行): テスト対象のメソッドを実行し、結果を受け取るフェーズ。
  • Assert(検証): 実行結果が期待通りであるかをアサーションメソッドで検証するフェーズ。

Arrange・Act・Assertの3フェーズをコメントや空行で明確に分けることで、どこで何を確かめているテストなのかが一目でわかります。

実務でよくある失敗は、1つのテストメソッドに複数のActとAssertを詰め込むことです。テストが失敗したとき、どのActが原因かわからなくなります。

1テスト1検証を心がけましょう。

java
@Test
void 足し算が正しくできるか検証する_AAAパターン() {
    // Arrange
    Calculator calculator = new Calculator();
    int a = 10;
    int b = 20;
    int expected = 30;

    // Act
    int actual = calculator.add(a, b);

    // Assert
    assertEquals(expected, actual);
}

テストクラスとテストメソッドの命名規則

テストメソッドの命名で理想的なのは、名前を読むだけで「何を」「どの状況で」「どう検証しているか」がわかることです。

  • テストクラス名: [テスト対象クラス名]Testとするのが一般的です。(例: CalculatorTest)
  • テストメソッド名: 英語ならshould[期待する結果]When[条件]のような形式があります。日本語なら「〇〇の場合は〇〇を返すこと」のように自然な文章で書くとわかりやすいです。

良い命名は、テストが失敗したときのエラーメッセージを読むだけで、問題の原因を推測する手助けとなります。

筆者のチームでは、日本語メソッド名を採用しています。IDEのテスト結果が一目で読めるため、コードレビューの効率が上がりました。

英語名か日本語名かはチームで統一することが大切です。

ユニットテスト(単体テスト)と統合テストの違い

JUnitが得意とする「ユニットテスト(単体テスト)」は、クラスやメソッドなど1つの部品を対象にします。

データベースやAPIなど外部への依存を切り離し、対象のロジックだけを検証します。

一方、「統合テスト」は複数のコンポーネントを組み合わせ、連携が正しく動くかを確認します。

ControllerからServiceを経由してDBにアクセスする一連の流れが典型例です。

まずはユニットテストをしっかり書くことが、品質保証の第一歩です。

あわせて読む
あわせて読む

ただし、ユニットテストだけでは検知できない不具合もある。

以前、同一クラス内のメソッド呼び出しでは@Transactionalが効かない(プロキシの限界)という問題に、本番でデータ不整合が発生して初めて気づいたことがある。

各メソッド単体のユニットテストはすべて通っていたのに、実際にサービス層を経由して呼び出す統合的な流れの中でだけ不具合が現れていた。

以降、サービス層の分割を徹底し、ユニットテストと統合テストの両方を書くようになった。

あわせて読む

テストが失敗したら、どう確認する?

テストを書き始めたばかりの頃は、テストが失敗した理由を読み解くこと自体でつまずきがちです。ここでは失敗時の確認手順を整理します。

アサーションエラーの読み方

assertEqualsが失敗すると、次のようなメッセージが表示されます。

text
org.opentest4j.AssertionFailedError: 2 + 3 は 5 になるべきです ==> expected: <5> but was: <6>

expected(期待値)とwas(実際の値)を見比べれば、どこがズレているかが一目でわかります。

assertEqualsの第3引数に渡したメッセージ(例:「2 + 3 は 5 になるべきです」)は、エラーの先頭に表示されます。

アサーションが複数あるテストほど、第3引数のメッセージを具体的に書いておくと原因を早く特定できます。

よくある失敗原因3パターン

失敗原因

  1. @BeforeAllと@BeforeEachの取り違え: テストごとにリセットすべきオブジェクトを@BeforeAll(クラス全体で1回だけ実行)で初期化してしまう。その結果、前のテストで変更された状態が次のテストに残る。
  2. テスト間での状態共有: 同じテストクラス内でstaticフィールドやモックの状態を複数のテストで使い回し、実行順序によって結果が変わる。
  3. 非同期処理のタイミングのズレ: 非同期処理の完了を待たずにアサーションを実行してしまい、処理が終わる前に検証してテストが不安定になる(いわゆるFlakyテスト)。

IntelliJ IDEAやEclipseでは、テスト結果画面で失敗したテストをクリックすると、該当するアサーション行にジャンプできます。

まずはエラーメッセージのexpectedとwasを見比べ、次に上記3パターンに該当していないかを確認する、という順番で切り分けると効率的です。

JUnit5の便利なアノテーション

基本をマスターしたら、テストをさらに効率化するアノテーションを使いましょう。

@BeforeEach / @AfterEach(初期化と後処理)

@BeforeEachは、各テストの前に共通の初期化処理を実行するアノテーションです。複数のテストで同じオブジェクト生成が必要なときに役立ちます。

同様に、@AfterEachは各テストメソッドの実行後に呼ばれ、リソースの解放などの後処理に使います。

java
class CalculatorTest {

    private Calculator calculator;

    @BeforeEach
    void setUp() {
        // 各テストの前にCalculatorインスタンスを生成する
        this.calculator = new Calculator();
        System.out.println("テスト準備完了");
    }

    @AfterEach
    void tearDown() {
        // 各テストの後に実行される
        System.out.println("テスト後処理完了");
    }

    @Test
    void testAdd() {
        assertEquals(5, calculator.add(2, 3));
    }

    @Test
    void testSubtract() {
        // assertEquals(1, calculator.subtract(3, 2));
    }
}

クラス全体で1回だけ実行したい処理には、@BeforeAllと@AfterAllを使います。DB接続の確立のような重い準備が典型例で、メソッドは原則staticにする必要があります。

4つのアノテーションが呼ばれる順番を図にすると、次のようになります。

@Testが2つあるクラスで呼ばれる順番

0回@BeforeEach の実行回数
0回@BeforeAll の実行回数
テストクラスのインスタンス①を新しく作る
インスタンス②を作り直す(①の状態は引き継がない)

各段をタップすると、その時点までの実行回数とコンソール出力を確認できます。

JUnit5は標準ではテストメソッドごとにテストクラスのインスタンスを新しく作ります。そのため@BeforeEachで初期化したフィールドは、テスト間で状態が引き継がれません。

@ParameterizedTestによるパラメータ化テスト

同じロジックのテストを、異なる入力値と期待値で何度も実行したいケースは多い。

@ParameterizedTestを使うと、テストコードの重複を避けられます。

@ValueSourceを使えば、簡単な値のリストをパラメータとして渡せます。

なお、@ParameterizedTestや@ValueSourceはjunit-jupiter-paramsに含まれます。導入方法の節で示したjunit-jupiter-apiとjunit-jupiter-engineだけでは入らないため、org.junit.jupiter:junit-jupiter-params(バージョンは同じ5.14.4)もtestスコープで追加してください。

java
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.assertTrue;

class StringUtilsTest {
    @ParameterizedTest
    @ValueSource(strings = {"", "  ", "\t"})
    void 文字列がブランクであるか検証する(String input) {
        assertTrue(input.isBlank());
    }
}

注意
この例で使ったString#isBlank()はJava 11以降のメソッドです。

Java 8環境ではinput.trim().isEmpty()に置き換えてください。

さらに、@CsvSourceを使えば、カンマ区切りの値で複数の引数(入力値と期待値など)を渡せます。

@DisplayNameでテスト名をわかりやすくする

JUnit5の@DisplayNameアノテーションを使うと、テスト結果のレポートに任意の名前を表示できます。

メソッド名が英語でも、レポートには日本語の説明を出せるので便利です。

java
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;

@DisplayName("Calculatorクラスのテスト")
class CalculatorTest {
    @Test
    @DisplayName("2 + 3 = 5 になること")
    void testAdd() {
        Calculator calculator = new Calculator();
        assertEquals(5, calculator.add(2, 3));
    }
}

テストメソッド名を日本語にする方法もありますが、@DisplayNameを使えばメソッド名は英語の命名規則に従いつつ、レポートだけ日本語にできます。

チーム開発ではこちらが推奨されるケースが多いです。

@Nestedによる階層的テスト構造

テスト対象クラスの機能が複雑になると、テストクラスも肥大化しがちです。

@Nestedアノテーションを使い、内部クラスでテストをグループ化すると、テストの構造がわかりやすくなります。

java
class BankAccountTest {

    // 口座の初期状態に関するテスト
    @Nested
    class 初期状態のテスト {
        @Test
        void 新規口座の残高は0円であること() {
            // ...
        }
    }

    // 入金機能に関するテスト
    @Nested
    class 入金テスト {
        @Test
        void 正の金額を入金すると残高が増えること() {
            // ...
        }

        @Test
        void 負の金額を入金しようとすると例外が発生すること() {
            // ...
        }
    }
}

関連するテストをまとめると、テスト結果もグループ単位で表示されるため、どの機能のテストが落ちたかをすぐ追えます。

@Disabledでテストを一時的に無効化する

未実装の機能に対応するテストや、環境依存で一時的に実行できないテストは、削除せずに@Disabledで無効化しておくと、後で対応漏れを防げます。

java
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

class CalculatorTest {
    @Test
    @Disabled("未実装のため一時的に無効化(担当: Taro, 対応予定: 次スプリント)")
    void 割り算の結果が正しいこと() {
        // ...
    }
}

@Disabledは前述の比較表にあるJUnit4の@Ignoreに対応するアノテーションです。

引数に無効化した理由を残しておくと、後でテストを見た人(未来の自分を含む)が状況を把握しやすくなります。

Mockitoを使ったモックテスト

実際の開発では、クラスが他のクラスや外部システム(データベース、APIなど)に依存していることがほとんどです。

依存先まで含めるとユニットテストの範囲を超えてしまいます。

そこで登場するのが「モック(Mock)」です。モックとは、本物のオブジェクトのふりをする偽物のオブジェクトのこと。

Mockitoなどのモックライブラリを使うと、モックを簡単に作成できます。

Mockitoの基本と導入方法

Mockitoは、Javaで最も人気のあるモックライブラリの一つです。

Mockitoを使うことで、依存オブジェクトの振る舞いを自由に定義し、テスト対象のロジックだけに集中できます。

導入はJUnitと同様に、ビルドツールに依存関係を追加するだけです。なお、Mockito 5系はJava 11以上が必要です(Java 8環境ではMockito 4系を使います)。

Mavenの場合 (pom.xml)

xml
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>5.23.0</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-junit-jupiter</artifactId>
    <version>5.23.0</version>
    <scope>test</scope>
</dependency>

依存オブジェクトはどうモック化する?

例えば、ユーザー情報をUserRepositoryから取得して処理を行うUserServiceをテストしたいとします。

java
// テスト対象クラス
public class UserService {
    private final UserRepository repository;

    public UserService(UserRepository repository) {
        this.repository = repository;
    }

    public String getUserName(int id) {
        User user = repository.findById(id);
        if (user != null) {
            return user.getName();
        }
        return "Unknown";
    }
}

UserRepositoryはデータベースにアクセスするため、ユニットテストではモックに置き換えます。

Mockitoを使えば、次のようにテストを書けます。

java
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class) // Mockito拡張を有効にする
class UserServiceTest {

    @Mock // UserRepositoryのモックを作成
    private UserRepository mockRepository;

    @InjectMocks // モックを注入してUserServiceのインスタンスを生成
    private UserService userService;

    @Test
    void 存在するユーザーIDの場合はユーザー名を返すこと() {
        // Arrange: モックの振る舞いを定義
        User user = new User(1, "Taro");
        // repository.findById(1)が呼ばれたら、作成したuserオブジェクトを返すように設定
        when(mockRepository.findById(1)).thenReturn(user);

        // Act: テスト対象メソッドを実行
        String userName = userService.getUserName(1);

        // Assert: 結果を検証
        assertEquals("Taro", userName);

        // Verify: repository.findById(1)が1回だけ呼ばれたことを検証
        verify(mockRepository, times(1)).findById(1);
    }
}

UserServiceTestの中で起きていること

テストUserServiceTest
UserServiceテスト対象
モック@Mock UserRepository呼出0回
DB本物は使わない接続0回

when(...).thenReturn(...)は、モックの振る舞い(スタブ:あらかじめ決めた値を返す設定)を定義するメソッドです。

verify(...)では、モックのメソッドが期待通りに呼ばれたかを検証します。

モックを使うことで、データベースに接続せずにUserServiceのロジックをテストできました。

モックを使うかどうかの判断基準はシンプルです。

テスト対象のロジックに集中したいとき、外部依存(DB・API・ファイルシステム)を切り離すためにモックを使います。

逆に、値の変換や計算だけを行うユーティリティクラスには、モックは不要です。

もう一つの判断軸として、「モックの数が増えすぎていないか」も見ておきたい。

1つのテストのために何個も@Mockを用意しなければならないなら、問題はテストの書きにくさだけではない。

テスト対象クラスが多くのクラスに依存しすぎている、という設計上のサインであることが多い。

モックが増えて辛くなったら、まずクラス設計を疑ってみる価値がある。

ここで紹介したのはMockitoの導入部分のみだ。spy・doReturn・ArgumentCaptorによる引数検証や静的メソッドのモック化は扱っていない。

Spring Bootの@MockitoBeanへの移行まで含めた内容は、Mockitoの使い方を解説した別記事で扱っている。

よくある質問

Q1. JUnit5とJUnit4の違いは何ですか?

A1. 最大の違いはアーキテクチャとアノテーション名です。JUnit4は単一jarでしたが、JUnit5はPlatform・Jupiter・Vintageの3モジュール構成になりました。初期化処理も@Before(JUnit4)から@BeforeEach(JUnit5)に変わっています。

Q2. JUnit5はどうやって導入しますか?

A2. MavenまたはGradleの依存関係にjunit-jupiter-apiとjunit-jupiter-engineを追加します(Gradleではjunit-platform-launcherも追加)。IntelliJ IDEAでGradleプロジェクトを新規作成した場合は、通常JUnitの依存関係が最初から入っています。

Q3. JUnit5でモックを使うにはどうすればいいですか?

A3. Mockitoライブラリを使います。@ExtendWith(MockitoExtension.class)でMockito拡張を有効にし、@Mockでモックを作成、@InjectMocksでテスト対象に注入します。外部依存(DB・APIなど)を切り離したいときに使います。

Q4. パラメータ化テストとは何ですか?

A4. 同じロジックを異なる入力値・期待値で繰り返しテストする仕組みです。JUnit5では@ParameterizedTestと@ValueSourceや@CsvSourceを組み合わせて使うと、テストコードの重複を避けられます。

Q5. @DisplayNameは何のために使いますか?

A5. テスト結果のレポートに任意の名前を表示するためのアノテーションです。メソッド名は英語の命名規則に従いつつ、レポートだけ日本語の説明にできるため、チーム開発で推奨されるケースが多いです。

Q6. テストを一時的にスキップするにはどうすればいいですか?

A6. @Disabledアノテーションを使います。JUnit4の@Ignoreに対応するもので、引数に無効化した理由を残しておけば、未実装機能や環境依存のテストを削除せずに一時停止できます。

Q7. JUnit6が出た今でもJUnit5を学ぶべきですか?

A7. はい、現実的な選択です。JUnit6は2025年9月30日に正式リリースされましたが、Java17以上が必須になったことや既存プロジェクトの移行コストから、2026年時点でも実務ではJUnit5(Jupiter)が広く使われています。

Q8. ユニットテストと統合テストの違いは何ですか?

A8. ユニットテストはクラスやメソッドなど1つの部品を対象に、外部依存を切り離してロジックだけを検証します。統合テストは複数のコンポーネントを組み合わせ、連携が正しく動くかを確認します。両方を書くことで、単体では見えない不具合も検知できます。

あわせて読む

まとめ

JUnitによるユニットテストは、Javaエンジニアの必須スキルです。

使い方の流れは、冒頭で示した3ステップに集約されます。依存関係を追加し、@Testとアサーションでテストを書き、mvn testやIDEで実行して結果を確認する。まずは手元のクラス1つで、この流れを一周させてみてください。

JUnitを使うと得られるメリット

  • コードの品質が向上する: 自動化されたテストが、バグの混入を未然に防ぎます。
  • 開発速度が上がる: 手動テストの時間を削減し、問題の早期発見により手戻りを減らせます。
  • 自信を持って開発できる: テストという安全網があるため、リファクタリングや機能追加を恐れずに行えます。
  • 仕様が明確になる: テストコードが、コードの振る舞いを定義するドキュメントとして機能します。

最初はテストコードに時間がかかります。

それでも、テストに使った時間は、修正のたびに手動確認を繰り返す手間の削減と、デグレードの早期発見という形で返ってきます。

JUnit5の基本を理解したら、次はSpring Boot 4での統合テスト(@SpringBootTest)や、カバレッジ計測ツール(JaCoCo)に挑戦してみてください。

REVISIONS / 改訂履歴
REV.DATE内容
42026-10-04記載内容の事実確認と修正
32026-08-212026年8月時点の情報で全体を見直し(古い記述を6件更新)
22026-05-212026年5月時点の情報で全体を見直し
12025-07-30初版
  • この記事を書いた人
  • 最新記事

トム

SESで約8年、環境を変えられないもどかしさの中にいました。32歳で自社開発企業に転職し、今はClaude Codeと自動化で、仕事も時間も組み直している途中です。
・Javaバックエンドエンジニア(Java歴12年以上)
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎
詳細なプロフィール

-Java入門