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

Java入門

【2026年版】JUnit5入門|Java単体テストの書き方を基礎から解説

トム

・都内自社開発企業勤務/Javaバックエンドエンジニア
/Java歴10年以上 ・首都圏在住30代
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎 詳細なプロフィール

JUnit5はJavaの単体テストで最も使われるフレームワークです。この記事では、JUnit5の導入方法から@Testアノテーション・アサーション・例外テストの基本、さらにMockitoを使ったモックテストまで、サンプルコード付きで解説します。

「テストコードの書き方がわからない」「JUnit4との違いが気になる」という方に向けて、現役エンジニアが実務で使うパターンを交えながら、明日からテストが書ける実践的な内容にまとめました。

この記事でわかること

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

JUnit5とは?Javaテストフレームワークの基本

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

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

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

ユニットテストの目的は、クラスやメソッドといったプログラムの最小単位(ユニット)が、それぞれ正しく機能するかを検証することにあります。

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

メリット

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

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

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

JUnit4とJUnit5では、アーキテクチャとアノテーション名が大きく変わりました。例えば、テスト前の初期化処理は、JUnit4では@Beforeでしたが、JUnit5では@BeforeEachに変わりました。

JUnit5は3つのモジュール(JUnit PlatformJUnit JupiterJUnit Vintage)で構成されており、拡張性が大幅に向上しました。2026年現在、次世代のJUnit 6もRC段階に入っていますが、実務では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

JUnitの導入方法

JUnitをプロジェクトに導入するのは非常に簡単です。特に、MavenやGradleといったビルドツールを利用すると、数行の設定を追加するだけで済みます。

Maven/Gradleによる導入

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

Mavenの場合 (pom.xml)

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

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

<scope>test</scope>は、このライブラリがテストコードのコンパイルと実行時にのみ必要であることを示します。JUnit5はJava 8以上で動作しますが、2026年現在はJava 17またはJava 21 LTSでの利用が一般的です。

Gradleの場合 (build.gradle)

build.gradleファイルに以下を記述します。

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.11.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.11.0'
}

test {
    useJUnitPlatform()
}

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

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

プロジェクト作成時のウィザードで「JUnit5」を選ぶだけで、必要な設定は自動で完了します。テストクラスを手動で作る場合も、IDEがJUnitの追加を提案してくれます。

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

JUnitの導入が完了したら、いよいよテストコードを書いていきましょう。ここでは、計算を行うシンプルなCalculatorクラスを例に、基本的なテストの書き方を解説します。

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

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

JUnit5では、テストしたいメソッドに@Testアノテーションを付けることで、そのメソッドがテストケースであることをフレームワークに伝えます。

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

import org.junit.jupiter.api.Test;

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

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

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

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

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であることを検証

例外の検証

特定の条件下で、メソッドが正しく例外をスローするかを検証したいケースもあります。JUnit5では、assertThrowsメソッドを使って簡単に例外テストを記述できます。

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引数には例外が出るはずの処理をラムダ式で渡します。意図した例外が正しく発生するかを安全にテストできます。

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

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

Arrange-Act-Assertパターンとは

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

AAAパターン

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

Arrange・Act・Assertの3フェーズをコメントや空行で明確に分けることで、テストコードの読みやすさは飛躍的に向上します。

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

@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にアクセスする一連の流れが典型例です。

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

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

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

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

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

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

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));
    }
}

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

同じロジックのテストを、異なる入力値と期待値で何度も実行したい場合があります。@ParameterizedTestを使うと、テストコードの重複を避けられます。

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

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());
    }
}

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

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

JUnit5の@DisplayNameアノテーションを使うと、テスト結果のレポートに任意の名前を表示できます。メソッド名が英語でも、レポートには日本語の説明を出せるので便利です。

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アノテーションを使い、内部クラスでテストをグループ化すると、テストの構造がわかりやすくなります。

class BankAccountTest {

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

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

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

関連するテストをまとめることで、読みやすさが格段に向上します。

Mockitoを使ったモックテスト

実際の開発では、クラスが他のクラスや外部システム(データベース、APIなど)に依存していることがほとんどです。依存先まで含めるとユニットテストの範囲を超えてしまいます。

そこで登場するのが「モック(Mock)」です。モックとは、本物のオブジェクトのふりをする偽物のオブジェクトのこと。Mockitoなどのモックライブラリを使うと、モックを簡単に作成できます。

Mockitoの基本と導入方法

Mockitoは、Javaで最も人気のあるモックライブラリの一つです。Mockitoを使うことで、依存オブジェクトの振る舞いを自由に定義し、テスト対象のロジックだけに集中できます。

導入はJUnitと同様に、ビルドツールに依存関係を追加するだけです。

Mavenの場合 (pom.xml)

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

依存オブジェクトのモック化と検証

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

// テスト対象クラス
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を使えば、次のようにテストを書けます。

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);
    }
}

when(...).thenReturn(...)でモックの振る舞い(スタブ:あらかじめ決めた値を返す設定)を定義し、verify(...)でモックのメソッドが期待通りに呼ばれたかを検証します。モックを使うことで、データベースに接続せずにUserServiceのロジックをテストできました。

モックを使うかどうかの判断基準はシンプルです。テスト対象のロジックに集中したいとき、外部依存(DB・API・ファイルシステム)を切り離すためにモックを使います。逆に、値の変換や計算だけを行うユーティリティクラスには、モックは不要です。

まとめ

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

JUnitを活用することで得られるメリット

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

最初はテストコードに時間がかかります。しかし、長期的に見れば、その投資は保守性の高い、堅牢なアプリケーションという形で何倍にもなって返ってきます。

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

  • この記事を書いた人
  • 最新記事

トム

・都内自社開発企業勤務/Javaバックエンドエンジニア
/Java歴10年以上 ・首都圏在住30代
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎 詳細なプロフィール

-Java入門