🧪 Spring Framework 講座【第38回】不具合を見逃さない!〜単体テスト(Unit Test)の基本〜

docs

前回までで、Spring Bootを使ったアプリケーション構築に必要な主要技術を網羅しました。しかし、どんなに優れたコードでも、**不具合(バグ)**はつきものです。

ここからは、アプリケーションの品質と信頼性を保証するための重要なプロセスであるテストについて学びます。今回は、テストの基本となる単体テスト(Unit Test)に焦点を当て、Javaの標準的なテストフレームワークであるJUnit 5の基本的な使い方を解説します。


1. なぜ単体テストが必要なのか?

単体テストとは、アプリケーションを構成する最小単位の部品(クラスやメソッドなど)が、単独で設計通りに正しく動作するかを確認するテストです。

  • 早期発見: 開発の初期段階で不具合を見つけられるため、修正コストが最も安く済みます。
  • 安全な改修: テストコードが整備されていると、既存の機能が壊れていないかをテストで確認できるため、安心してコードの改修やリファクタリングが行えます(回帰テスト)。
  • 仕様の明確化: テストコードは、「そのコードが何をすべきか」という実行可能な仕様書の役割を果たします。

2. Javaの標準テストフレームワーク:JUnit 5

Spring Bootでは、標準でJUnit 5(Jupiter)が導入されており、すぐに利用できます。

2-1. 基本的なアノテーション

JUnit 5を使ったテストクラスの基本的な構造とアノテーションです。

アノテーション用途
@Testテストメソッドであることを示します。このメソッドがテストランナーによって実行されます。
@BeforeEach各テストメソッドが実行される直前に実行されます。テストデータの初期化などに使用。
@AfterEach各テストメソッドが実行された直後に実行されます。後処理などに使用。
@DisplayNameテストクラスやテストメソッドに、日本語などの分かりやすい名前を付けられます。

2-2. テストクラスの構成例

Java

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*; // アサーションメソッドを利用

@DisplayName("計算サービスクラスのテスト")
class CalculatorTest {

    private Calculator calculator; // テスト対象のクラス

    @BeforeEach
    void setUp() {
        // テスト前にCalculatorインスタンスを初期化
        calculator = new Calculator(); 
    }

    @AfterEach
    void tearDown() {
        // 各テスト後の後処理(ここでは何もしない)
    }

    @Test
    @DisplayName("2つの数値の加算が正しく行われること")
    void testAdd() {
        // 1. 実行(Actual: 実際の結果)
        int result = calculator.add(1, 2); 
        
        // 2. 検証(Expected: 期待される結果)
        // 実際の結果と期待される結果が一致するかを検証
        assertEquals(3, result, "1 + 2 は 3 になるはずです"); 
    }

    @Test
    @DisplayName("ゼロ除算で例外が発生すること")
    void testDivideByZero() {
        // 特定の例外がスローされることを検証
        assertThrows(IllegalArgumentException.class, () -> {
            calculator.divide(10, 0);
        });
    }
}

3. 検証(アサーション):Assertionsクラス

テストにおいて最も重要なのが、検証(Assertion)です。JUnit 5では、org.junit.jupiter.api.Assertions クラスに用意された静的メソッドを使います。

メソッド用途
assertEquals(expected, actual, message)期待値と実際の結果が等しいことを検証
assertTrue(condition)条件が true であることを検証
assertFalse(condition)条件が false であることを検証
assertNull(actual)値が null であることを検証
assertThrows(type, executable)指定されたコード実行時に特定の例外がスローされることを検証

4. 単体テストの原則:依存関係の排除

単体テストの目的は、その部品(クラス)単独の動作を検証することです。

もしテスト対象の Service クラスが Repository クラスに依存していた場合、テスト中にDBアクセスが発生してしまいます。これでは、DBの状態によってテストが失敗する可能性があり、純粋な単体テストとは言えません。

原則: 単体テストでは、外部への依存関係(DB、外部API、DIコンテナ)をすべて排除し、メモリ上だけで完結させるべきです。

この依存関係を排除するために、**モック(Mock)スタブ(Stub)**という技術が必要になります。

✅ 本日のまとめ

  • **単体テスト(Unit Test)**は、最小単位(クラス、メソッド)のコードの動作を検証し、早期に不具合を発見して改修の安全性を高める。
  • Spring Bootでは、標準でJUnit 5を使用する。
  • @Test でテストメソッドを定義し、Assertions クラスのメソッド(assertEqualsassertThrows など)で検証を行う。
  • 純粋な単体テストのためには、テスト対象のクラスから外部への依存関係(DB、外部APIなど)を排除する必要がある。

🔔 次回予告

単体テストの原則として学んだ「依存関係の排除」を実現するためには、どうすれば良いでしょうか。

次回は、テスト時に依存関係を偽物に置き換えるための強力な技術、モック(Mock)とスタブ(Stub)、そしてその実装に不可欠なライブラリ Mockito の使い方を学びます。

次回:【第39回】依存を断ち切る!〜モックとMockitoによる単体テスト〜 にご期待ください!

コメント

タイトルとURLをコピーしました