はじめに
Javaのテストフレームワーク JUnit 5 の使い方を、基礎から丁寧に解説します。
「テストを書いたことがない」「JUnitって何?」という方でも、この記事を読めば 自分でテストを書いて実行できる ようになります。
JUnitとは
JUnitは、Javaで最も広く使われているテストフレームワーク です。
| 項目 | 内容 |
|---|---|
| 正式名称 | JUnit 5(Jupiter) |
| 用途 | ユニットテスト(単体テスト) |
| メリット | コードの品質を保証し、バグを早期に発見できる |
基本の4ステップ
ステップ1:テスト対象のクラスを作る
まずは、テストしたいクラスを用意します。
package com.example.calculator;
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public int subtract(int a, int b) {
return a - b;
}
public int multiply(int a, int b) {
return a * b;
}
public int divide(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("0で割ることはできません");
}
return a / b;
}
}
ステップ2:テストクラスを作る
テスト対象クラスに対応するテストクラスを作成します。
package com.example.calculator;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.*;
class CalculatorTest {
private Calculator calculator;
@BeforeEach
void setUp() {
// 各テストの前にインスタンスを作成
calculator = new Calculator();
}
@Test
@DisplayName("2 + 3 = 5 になること")
void testAdd() {
int result = calculator.add(2, 3);
assertEquals(5, result, "2 + 3 は 5 になるべき");
}
@Test
@DisplayName("5 - 3 = 2 になること")
void testSubtract() {
int result = calculator.subtract(5, 3);
assertEquals(2, result, "5 - 3 は 2 になるべき");
}
@Test
@DisplayName("4 × 3 = 12 になること")
void testMultiply() {
int result = calculator.multiply(4, 3);
assertEquals(12, result, "4 × 3 は 12 になるべき");
}
@Test
@DisplayName("10 ÷ 2 = 5 になること")
void testDivide() {
int result = calculator.divide(10, 2);
assertEquals(5, result, "10 ÷ 2 は 5 になるべき");
}
@Test
@DisplayName("0で割ると例外が発生すること")
void testDivideByZero() {
assertThrows(IllegalArgumentException.class, () -> {
calculator.divide(10, 0);
});
}
}
ステップ3:テストを実行する
IDEで右クリック → 「Run Tests」、またはコマンドラインで実行します。
# Maven
mvn test
# Gradle
gradle test
ステップ4:結果を確認する
✅ testAdd - PASSED
✅ testSubtract - PASSED
✅ testMultiply - PASSED
✅ testDivide - PASSED
✅ testDivideByZero - PASSED
5 tests completed, 0 failed
全てグリーンになればOKです。失敗したテストがあれば、テスト対象のコードかテストコードを修正しましょう。
テストが失敗する場合
意図的に失敗するテストを書いて、JUnitの出力を確認してみましょう。
@Test
@DisplayName("意図的に失敗させるテスト")
void testAddFail() {
Calculator calculator = new Calculator();
int result = calculator.add(8, 3);
assertEquals(5, result, "8 + 3 の結果が 5 ではないので失敗する");
}
実行結果
❌ testAddFail - FAILED
org.opentest4j.AssertionFailedError: 8 + 3 の結果が 5 ではないので失敗する
Expected: 5
Actual : 11
JUnitは 期待値(Expected)と実際の値(Actual) を表示してくれるので、何が間違っているかすぐにわかります。
よく使うアノテーション一覧
テスト実行系
| アノテーション | 用途 | 実行タイミング |
|---|---|---|
@Test |
テストメソッドとして認識させる | テスト実行時 |
@DisplayName |
テスト名を日本語で表示 | テスト結果に表示 |
@Disabled |
テストを一時的にスキップ | 実行しない |
@Test
@DisplayName("ユーザー名が空の場合、エラーになること")
void testEmptyUsername() {
// テストコード
}
@Test
@Disabled("バグ #123 の修正待ち")
void testSomethingBroken() {
// 一時的にスキップ
}
ライフサイクル系
| アノテーション | 用途 | 実行回数 |
|---|---|---|
@BeforeEach |
各テストの前に実行 | テストの数だけ |
@AfterEach |
各テストの後に実行 | テストの数だけ |
@BeforeAll |
全テストの前に1回だけ実行 | 1回 |
@AfterAll |
全テストの後に1回だけ実行 | 1回 |
class UserServiceTest {
@BeforeAll
static void initAll() {
System.out.println("=== テスト開始 ===");
// DB接続など、重い初期化処理
}
@BeforeEach
void init() {
System.out.println("--- テストメソッド開始 ---");
// テストデータの準備
}
@AfterEach
void tearDown() {
System.out.println("--- テストメソッド終了 ---");
// テストデータのクリーンアップ
}
@AfterAll
static void tearDownAll() {
System.out.println("=== テスト終了 ===");
// DB切断など
}
@Test
void test1() {
System.out.println("test1 実行中");
}
@Test
void test2() {
System.out.println("test2 実行中");
}
}
実行順序
=== テスト開始 === ← @BeforeAll
--- テストメソッド開始 --- ← @BeforeEach
test1 実行中 ← @Test
--- テストメソッド終了 --- ← @AfterEach
--- テストメソッド開始 --- ← @BeforeEach
test2 実行中 ← @Test
--- テストメソッド終了 --- ← @AfterEach
=== テスト終了 === ← @AfterAll
よく使うアサーション一覧
アサーションは 「期待した結果になっているか」を検証する メソッドです。
基本のアサーション
// 値が等しいか
assertEquals(期待値, 実際の値);
assertEquals(5, calculator.add(2, 3));
// 値が等しくないか
assertNotEquals(0, calculator.add(2, 3));
// trueか
assertTrue(result > 0);
// falseか
assertFalse(list.isEmpty());
// nullか
assertNull(user.getMiddleName());
// nullでないか
assertNotNull(user.getId());
例外のテスト
// 例外が発生することを検証
assertThrows(IllegalArgumentException.class, () -> {
calculator.divide(10, 0);
});
// 例外メッセージも検証
Exception exception = assertThrows(IllegalArgumentException.class, () -> {
calculator.divide(10, 0);
});
assertEquals("0で割ることはできません", exception.getMessage());
複数の検証をまとめて実行
// 1つ失敗しても全て実行される
assertAll("ユーザー情報の検証",
() -> assertEquals("太郎", user.getName()),
() -> assertEquals("taro@example.com", user.getEmail()),
() -> assertEquals(25, user.getAge())
);
実践的なテストパターン
パラメータ化テスト
同じテストを複数の値で実行したいときに便利です。
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
class CalculatorTest {
@ParameterizedTest
@DisplayName("足し算のパラメータ化テスト")
@CsvSource({
"1, 1, 2",
"2, 3, 5",
"10, -5, 5",
"0, 0, 0",
"-3, -7, -10"
})
void testAddParameterized(int a, int b, int expected) {
Calculator calculator = new Calculator();
assertEquals(expected, calculator.add(a, b));
}
}
1つのテストメソッドで 5パターン のテストが実行されます。
ネストテスト
テストをグループ化して整理できます。
@DisplayName("Calculatorのテスト")
class CalculatorTest {
private Calculator calculator;
@BeforeEach
void setUp() {
calculator = new Calculator();
}
@Nested
@DisplayName("addメソッド")
class AddTest {
@Test
@DisplayName("正の数同士の足し算")
void testPositiveNumbers() {
assertEquals(5, calculator.add(2, 3));
}
@Test
@DisplayName("負の数を含む足し算")
void testNegativeNumbers() {
assertEquals(-1, calculator.add(2, -3));
}
@Test
@DisplayName("0を含む足し算")
void testWithZero() {
assertEquals(3, calculator.add(3, 0));
}
}
@Nested
@DisplayName("divideメソッド")
class DivideTest {
@Test
@DisplayName("正常な割り算")
void testNormalDivide() {
assertEquals(5, calculator.divide(10, 2));
}
@Test
@DisplayName("0で割ると例外発生")
void testDivideByZero() {
assertThrows(IllegalArgumentException.class, () -> {
calculator.divide(10, 0);
});
}
}
}
テスト結果がツリー構造で表示され、とても見やすくなります。
Calculatorのテスト
├── addメソッド
│ ├── ✅ 正の数同士の足し算
│ ├── ✅ 負の数を含む足し算
│ └── ✅ 0を含む足し算
└── divideメソッド
├── ✅ 正常な割り算
└── ✅ 0で割ると例外発生
テストを書くときのコツ
1. テストメソッド名は「何をテストしているか」がわかるように
// 悪い例
@Test
void test1() { ... }
// 良い例
@Test
@DisplayName("メールアドレスが空の場合、バリデーションエラーになること")
void testEmailValidation_empty_shouldFail() { ... }
2. 1つのテストで1つのことだけ検証する
// 悪い例:複数のことを1つのテストで検証
@Test
void testCalculator() {
assertEquals(5, calculator.add(2, 3));
assertEquals(2, calculator.subtract(5, 3));
assertEquals(12, calculator.multiply(4, 3));
}
// 良い例:1テスト1検証
@Test
void testAdd() {
assertEquals(5, calculator.add(2, 3));
}
@Test
void testSubtract() {
assertEquals(2, calculator.subtract(5, 3));
}
3. テストの構造は「AAA パターン」で書く
@Test
void testUserRegistration() {
// Arrange(準備)
UserService service = new UserService();
String email = "test@example.com";
String password = "Password1";
// Act(実行)
User user = service.register(email, password);
// Assert(検証)
assertNotNull(user.getId());
assertEquals(email, user.getEmail());
}
4. テストは独立させる
各テストが他のテストに依存しないようにしましょう。@BeforeEach で毎回初期化するのがポイントです。
まとめ
| カテゴリ | 覚えること |
|---|---|
| 基本アノテーション |
@Test, @DisplayName, @Disabled
|
| ライフサイクル |
@BeforeEach, @AfterEach, @BeforeAll, @AfterAll
|
| アサーション |
assertEquals, assertTrue, assertThrows, assertAll
|
| 実践テクニック | パラメータ化テスト、ネストテスト |
| 書き方のコツ | AAAパターン、1テスト1検証、わかりやすい命名 |
まずは簡単なクラスから 実際にテストを書いてみる ことが一番の学習方法です。
この記事が、JUnitを始めるきっかけになれば嬉しいです!
著者: @kotaro_ai_lab
AI駆動開発やテック情報を毎日発信しています。フォローお気軽にどうぞ!