1.

Spring Bootのテスト入門|@SpringBootTestとJUnitでの単体・結合テスト

編集

Spring Framework(Spring Boot)のテストとは、JUnit と Spring が提供するテスト支援機能(Spring Test)を組み合わせて、アプリケーションの動作を自動で検証する仕組みです。コントローラやサービスといった個々の部品を単体で確かめる単体テストから、DI コンテナを実際に起動して複数の部品の連携を確かめる結合テストまで、同じ枠組みの中で記述できます。本稿では Spring Boot を前提に、テストの基本的な考え方・依存設定・主要アノテーション・サンプルコード・実行方法・つまずきやすい点を順に解説します。

この記事の要点
  • Spring Boot のテストは JUnit をテスト実行基盤とし、Spring Test(spring-test)が DI コンテナの起動やモックを支援する。
  • 結合テストでは @SpringBootTest でアプリケーション全体のコンテキストを起動し、Bean を @Autowired で取得して検証できる。
  • 必要な依存は spring-boot-starter-test 1 つで、JUnit・AssertJ・Mockito・Spring Test などがまとめて入る。
  • 単体テストは特定クラス単独、結合テストは複数部品の連携を確認する。目的に応じて使い分ける。
  • Web 層だけ・JPA 層だけといったスライステスト@WebMvcTest / @DataJpaTest)を使うと、起動範囲を絞って高速化できる。
  • 実行は IDE のテスト実行機能のほか、mvn test(Maven)や gradle test(Gradle)で行える。

 

Spring Boot におけるテストの全体像

Java のテストでは JUnit(JUnit 5 / JUnit Jupiter が広く使われています)がテストの実行基盤を担います。テストメソッドの起動やアサーション(期待値の検証)は JUnit の役割です。

一方、Spring アプリケーションのテストでは「DI コンテナ(ApplicationContext)を起動して Bean を取得したい」「コントローラへの HTTP リクエストを擬似的に投げたい」といった、Spring 特有の事情が出てきます。これを支援するのが Spring Test(spring-test モジュール)です。Spring Boot ではさらに @SpringBootTest などの便利なアノテーションが用意され、設定の自動検出やテスト用コンテキストの構築が簡単になっています。

つまり、おおまかには次の関係になります。

  • JUnit … テストを動かす土台。@Test を付けたメソッドが実行される。
  • Spring Test / Spring Boot Test … テストの中で Spring のコンテキストを立ち上げ、Bean の注入やモックを行う。
  • Mockito / AssertJ … モック(代役オブジェクト)作成や、読みやすいアサーションを担う補助ライブラリ。

 

依存関係(spring-boot-starter-test)

Spring Boot では、テストに必要なライブラリは spring-boot-starter-test というスターターにまとめられています。このスターターには JUnit Jupiter、Spring Test、AssertJ、Mockito、JSONassert などが含まれており、個別にバージョンを指定する必要はありません。Spring Initializr でプロジェクトを生成すると、通常は最初から追加されています。

Maven(pom.xml)の場合は次のように記述します。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

Gradle(build.gradle)の場合は次のように記述します。

testImplementation 'org.springframework.boot:spring-boot-starter-test'

scopetestImplementation によって、これらのライブラリはテスト時のみ使われ、本番の成果物には含まれません。

 

単体テストと結合テストの違い

テストは目的や対象範囲によって、大きく単体テストと結合テストに分けて考えると整理しやすくなります。

観点 単体テスト 結合テスト
対象範囲 特定のクラスやメソッド単体 複数の部品やレイヤーの連携
依存先の扱い モックなどで置き換えることが多い 実際の Bean を使って動かすことが多い
Spring コンテキスト 起動しないこともある(純粋な JUnit + Mockito) @SpringBootTest などで起動することが多い
実行速度 速い傾向 コンテキスト起動を伴うと遅くなりやすい

サービスクラスのロジックだけを確認したい場合は、依存をモック化した単体テストが向きます。逆に「コントローラ → サービス → リポジトリ」と一連の流れが正しくつながるかを確認したい場合は結合テストが適しています。なお、ここでの分類は一般的な目安であり、現場やチームによって呼び方や線引きが異なる場合があります。

 

主要なテスト用アノテーション

Spring Boot のテストでよく使うアノテーションを整理します。用途に応じて組み合わせて使います。

アノテーション 主な用途
@SpringBootTest アプリケーション全体のコンテキストを起動する。結合テストの基本。
@Test JUnit のテストメソッドを示す。1 つの検証単位を表す。
@Autowired テスト対象の Bean をコンテキストから注入する。
@MockBean コンテキスト内の Bean をモックに差し替える(依存先を擬似化)。
@WebMvcTest Web(MVC)層だけを対象にしたスライステスト。
@DataJpaTest JPA リポジトリ層だけを対象にしたスライステスト。

このうち @SpringBootTest はコンテキスト全体を起動するため確実ですが起動コストが高く、@WebMvcTest@DataJpaTest は必要な層だけを読み込むため軽量です。@MockBean は、テスト対象が依存しているコンポーネント(外部サービスやリポジトリなど)を本物ではなくモックに置き換えたいときに使います。なお、Spring Boot のバージョンによっては @MockBean が非推奨となり、後継アノテーションが推奨されている場合があるため、利用中のバージョンのリファレンスを確認してください。

 

簡単なテストクラスの例

まずは、コンテキストが正しく起動するかを確認する最小限の結合テストです。Spring Initializr でプロジェクトを作成すると、これに近いテストクラスが自動生成されます。

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class DemoApplicationTests {

    @Test
    void contextLoads() {
        // コンテキストが正常に起動すれば成功
    }
}

次に、サービスクラスのロジックを検証する例です。@SpringBootTest でコンテキストを起動し、@Autowired でテスト対象の Bean を注入しています。assertEquals は JUnit のアサーションで、期待値と実際の値が一致するかを確認します。

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

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class CalcServiceTest {

    @Autowired
    private CalcService calcService;

    @Test
    void add_正の数同士を加算できる() {
        int result = calcService.add(2, 3);
        assertEquals(5, result);
    }
}

依存先を実際には動かしたくない場合は、@MockBean でモックに差し替えます。たとえばリポジトリへのアクセスを擬似化し、サービスのロジックだけを切り出して検証できます。

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

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;

@SpringBootTest
class UserServiceTest {

    @Autowired
    private UserService userService;

    @MockBean
    private UserRepository userRepository;

    @Test
    void findName_リポジトリの戻り値を返す() {
        when(userRepository.findNameById(1L)).thenReturn("tarou");
        String name = userService.findName(1L);
        assertEquals("tarou", name);
    }
}

Web 層だけを対象にしたい場合は @WebMvcTest を使い、MockMvc でコントローラに擬似的な HTTP リクエストを送って結果を検証します。コントローラが依存するサービスは @MockBean で差し替えます。

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;

@WebMvcTest(HomeController.class)
class HomeControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void トップページは200を返す() throws Exception {
        mockMvc.perform(get("/"))
             .andExpect(status().isOk());
    }
}

 

テストの実行方法

作成したテストは、IDE またはビルドツールから実行できます。

IDE から実行する

Eclipse や IntelliJ IDEA などの IDE では、テストクラスやテストメソッドを右クリックし、JUnit としての実行を選ぶだけでテストが動きます。下の画面のように、プロジェクトを右クリックして実行メニューをたどると、各種の実行構成を選択できます。

テスト結果は IDE のテストビューに、成功・失敗の件数とともに一覧表示されます。失敗したテストを選ぶと、期待値と実際の値の差分やスタックトレースを確認できます。

Maven から実行する

Maven プロジェクトでは、プロジェクトのルートで次のコマンドを実行すると、テストフェーズが走ります。

mvn test

IDE 上で Maven のゴールを指定して実行することもできます。下の画面のように実行構成を開き、ゴール欄に実行したい処理(例として testpackage)を入力して実行します。package を指定した場合も、設定によってはビルドの過程でテストが実行されます。

ゴールを入力して実行すると、ビルドとテストが順に進みます。

すべてのテストが通り、最終的に BUILD SUCCESS が表示されれば成功です。テストが 1 つでも失敗すると、既定ではビルドが失敗(BUILD FAILURE)となり、どのテストがなぜ失敗したかがログに出力されます。

上のように、実行結果のレポートでテストの成否や件数を確認できます。Maven の場合、詳細なテストレポートは target/surefire-reports ディレクトリにも出力されます。

Gradle から実行する

Gradle プロジェクトでは、次のコマンドでテストを実行します。

gradle test

Gradle Wrapper を使う場合は、Windows では gradlew test、Unix 系では ./gradlew test のように実行します。テスト結果の HTML レポートは build/reports/tests ディレクトリに生成されます。

 

つまずきやすいポイント

注意点 対処の方向性
@SpringBootTest は起動が重い アプリ全体のコンテキストを立ち上げるため、テスト数が増えると時間がかかる。すべてを @SpringBootTest で書く必要はない。
スライステストを活用していない Web 層なら @WebMvcTest、JPA 層なら @DataJpaTest のように対象を絞ると、起動範囲が小さくなり高速化しやすい。
外部依存をそのまま呼んでしまう DB や外部 API への実アクセスはテストを不安定・低速にしがち。@MockBean やモックで切り離し、検証したいロジックに集中する。
テスト間で状態が共有される DB を使う結合テストでは、前のテストのデータが残ると結果が変わることがある。トランザクションのロールバックや初期化で、各テストを独立させる。

基本方針として、まず軽量な単体テスト・スライステストで広く検証し、全体の連携を確認したい重要な経路にだけ @SpringBootTest による結合テストを用いる、という使い分けが扱いやすくなります。

 

よくある質問(FAQ)

Q1. JUnit と Spring Test は何が違うのですか?

JUnit はテストを実行する汎用的な基盤で、Spring に限らず Java 全般のテストで使われます。Spring Test(および Spring Boot Test)は、その JUnit の上で「DI コンテナを起動して Bean を注入する」「コントローラへ擬似リクエストを送る」といった Spring 特有の機能を提供する位置づけです。両者は競合するものではなく、組み合わせて使います。

Q2. すべてのテストを @SpringBootTest で書くべきですか?

必ずしもそうではありません。コンテキスト全体を起動する @SpringBootTest は確実な反面、起動コストが高くなります。単一クラスのロジックだけを確認したい場合は、Spring を起動しない純粋な JUnit + Mockito の単体テストや、対象層を絞ったスライステストの方が高速で扱いやすいことが多いです。検証したい範囲に合わせて選ぶのが基本です。

Q3. テストが失敗したかどうかはどこで分かりますか?

IDE で実行した場合はテストビューに成功・失敗が色や件数で表示され、失敗したテストを選ぶと差分やスタックトレースを確認できます。コマンドで実行した場合は、Maven なら target/surefire-reports、Gradle なら build/reports/tests にレポートが出力されます。ビルドの標準出力でも、失敗したテスト名と原因の概要を確認できます。

編集
Post Share
子ページ

子ページはありません

同階層のページ

同階層のページはありません

最近更新/作成されたページ