0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Spring Bootのテスト、種類が多すぎるので全体像を整理する

0
Posted at

Spring Bootでテストを書いていると、さまざまな用語やツールが登場します。

JUnit
Mockito
MockMvc
@SpringBootTest
@WebMvcTest
@DataJpaTest

最初に学習したとき、

結局どれが何をテストしているのか?

という部分がかなり分かりづらく感じました。

そこでこの記事では、細かい書き方に入る前に、Spring Bootにおけるテストの全体像を整理します。


まずSpring Bootアプリの構造を確認する

一般的なSpring Bootアプリケーションでは、次のように役割を分けます。

HTTP Request
     |
     v
+------------+
| Controller |
+------------+
     |
     v
+------------+
|  Service   |
+------------+
     |
     v
+------------+
| Repository |
+------------+
     |
     v
+------------+
|  Database  |
+------------+

ざっくり言えば、

層 主な役割
Controller HTTPリクエストを受け取る
Service ビジネスロジックを実行する
Repository DBへアクセスする
Database データを保存する

となります。

テストでは、

このうち、どこまでをまとめて動かすのか?

という考え方が重要になります。


単体テストとは

単体テスト(Unit Test)は、クラスやメソッドなどの小さな単位を独立してテストするものです。

例えばServiceをテストするとします。

Controller
     |
     X

+-----------+
|  Service  | ← ここをテスト
+-----------+
     |
     v

Repository

ServiceはRepositoryを利用します。

しかしServiceだけをテストしたいのに、本物のDBまで必要になるとテストが大きくなってしまいます。

そこでRepositoryを**Mock(モック)**に置き換えます。

+-----------+
|  Service  | ← 本物
+-----------+
     |
     v
+-----------+
| Mock Repo | ← 偽物
+-----------+

これによって、

Repositoryがこの値を返した場合、Serviceは正しく処理できるか?

をServiceだけに絞って確認できます。


Mockとは

Mockは、テストで本物の依存オブジェクトの代わりとして利用するオブジェクトです。

例えば本物のRepositoryなら、

Service
   |
   v
Repository
   |
   v
Database

となります。

Mockを使うと、

Service
   |
   v
Mock Repository

となります。

そして、

when(repository.findById(1L))
        .thenReturn(Optional.of(equipment));

のように、

findById(1L)が呼ばれたら、このEquipmentを返してね

と振る舞いを決められます。


JUnitとは

JUnitは、Javaでテストを書くためのテスティングフレームワークです。

例えば、

@Test
void addすると2つの数値を加算できる() {
    int result = calculator.add(10, 20);

    assertEquals(30, result);
}

のようなテストを書けます。

JUnitは、

@Test
テストの実行
アサーション
テストのライフサイクル

など、Javaでテストを書くための基本的な仕組みを提供します。

つまり、

JUnit = Javaでテストを書くための土台

と考えると分かりやすいです。


Mockitoとは

Mockitoは、JavaでMockを作成するためのライブラリです。

例えば、

@Mock
private EquipmentRepository equipmentRepository;

とすると、RepositoryのMockを作成できます。

さらに、

when(equipmentRepository.findAll())
        .thenReturn(List.of(equipment));

のようにMockの振る舞いを設定できます。

つまり、

JUnit
↓
テストを書く・実行する

Mockito
↓
テストに必要なMockを作る

という役割の違いがあります。

JUnitとMockitoは競合するものではなく、一緒に利用することが多いです。


Controllerを直接呼ぶ単体テスト

Controllerも普通のJavaクラスなので、直接メソッドを呼び出してテストできます。

例えば、

DashboardController controller =
        new DashboardController(mockService);

controller.dashboard("pump", model);

のような形です。

この場合、

HTTP
 X

Controller ← 直接呼び出す
    |
    v
Mock Service

となります。

Controller自身のロジックを素早く確認できます。

ただし、実際のHTTPリクエストは通していません。

そのため、

GET /dashboard

というHTTPリクエストとして正しく処理できるかまでは確認できません。


MockMvcとは

MockMvcは、Spring MVCのWeb層をテストするための仕組みです。

例えば、

mockMvc.perform(get("/dashboard"))
       .andExpect(status().isOk())
       .andExpect(view().name("dashboard"));

のように書けます。

イメージとしては、

疑似HTTP Request
       |
       v
Spring MVC
       |
       v
Controller

です。

Controllerのメソッドを直接呼び出すのではなく、

GET /dashboard

というHTTPリクエストに近い形でテストできます。

そのため、

  • URL
  • HTTPメソッド
  • ステータスコード
  • View名
  • Model
  • バリデーション

など、Web層の振る舞いを確認できます。


@WebMvcTestとは

@WebMvcTestは、主にSpring MVCのWeb層に絞ってテストするときに利用します。

例えば、

@WebMvcTest(DashboardController.class)
class DashboardControllerTest {
}

とします。

イメージとしては、

Spring Boot Application

+-------------------+
| Controller        | ← 起動
| Spring MVC        | ← 起動
+-------------------+

Service              ← Mockなどにする
Repository           ← 基本対象外
Database             ← 基本対象外

となります。

アプリケーション全体を起動するより対象を絞れるため、Controller周辺のテストに向いています。


Repositoryのテスト

Repositoryでは、実際にDBとのやり取りが正しくできるかを確認したい場合があります。

例えば、

List<Equipment> result =
    repository.findByNameContainingIgnoreCase("pump");

という検索が正しく動くか確認するとします。

この場合、RepositoryをMockにしてしまうと意味がありません。

Repository ← 本物
     |
     v
Database   ← テスト用DB

として確認する必要があります。


@DataJpaTestとは

JPA Repositoryのテストで利用できるのが@DataJpaTestです。

@DataJpaTest
class EquipmentRepositoryTest {
}

JPA関連のコンポーネントを中心に読み込み、RepositoryとDBの連携をテストできます。

例えば、

EquipmentRepository
        |
        v
      JPA
        |
        v
   Test Database

という範囲を確認できます。

ServiceやControllerまで含める必要はありません。


@SpringBootTestとは

@SpringBootTestは、Spring BootのApplicationContextを広い範囲で読み込んでテストします。

イメージとしては、

Controller
    |
    v
Service
    |
    v
Repository
    |
    v
Database

をまとめて動かすテストに利用できます。

例えば、

@SpringBootTest
class EquipmentApplicationTest {
}

のように使います。

対象範囲が広い分、

単体テスト
↓
速い・対象が小さい

@SpringBootTest
↓
比較的重い・対象が広い

という違いがあります。


結合テストとは

結合テスト(Integration Test)は、複数のコンポーネントを組み合わせて正しく動くか確認するテストです。

例えば、

Controller
    |
    v
Service
    |
    v
Repository

を組み合わせて確認します。

単体では正しく動いていても、

Controller → Service

Service → Repository

の接続部分に問題がある可能性があります。

そのため、単体テストとは別に結合した状態でも確認します。


E2Eテストとは

E2EはEnd-to-Endの略です。

ユーザーが実際に利用する流れに近い形で、システム全体を確認します。

例えば、

Browser
   |
   v
HTTP
   |
   v
Spring Boot
   |
   v
Database

まで通します。

例えば設備管理システムなら、

ログイン
↓
設備一覧を表示
↓
設備を検索
↓
点検結果を登録
↓
登録結果を確認

といった一連の操作をテストできます。

対象範囲が広い分、実行時間やテスト環境の準備も大きくなります。


テストはピラミッドで考える

テストには「テストピラミッド」という考え方があります。

             /\
            /  \
           / E2E\
          /------\
         / 結合   \
        /----------\
       /   単体     \
      /______________\

一般的には、

単体テスト
→ 多い・速い

結合テスト
→ 中くらい

E2E
→ 少ない・重い

というバランスを取ります。

すべてをE2Eで確認すると、テストの実行に時間がかかります。

逆に単体テストだけでは、コンポーネント同士を接続したときの問題を見つけられません。

そのため、それぞれを組み合わせます。


Spring Bootのテストを整理すると

ここまでをまとめると、ざっくり次のようになります。

ツール・アノテーション 主な役割
JUnit Javaでテストを書くための基本フレームワーク
Mockito Mockを作成する
MockMvc Spring MVCのWeb層をHTTPに近い形でテストする
@WebMvcTest Web層を中心にテストする
@DataJpaTest JPA / Repositoryを中心にテストする
@SpringBootTest Spring Bootの広い範囲を読み込んでテストする

そして、アプリケーションの各層に当てはめると、

HTTP
 |
 v
Controller
 |   ↑
 |   @WebMvcTest + MockMvc
 |
 v
Service
 |   ↑
 |   JUnit + Mockito
 |
 v
Repository
 |   ↑
 |   @DataJpaTest
 |
 v
Database

----------------------------

全部まとめて確認
        ↑
@SpringBootTestなど

というイメージになります。


何をテストしたいかを先に考える

Spring Bootのテストでは、

とりあえず@SpringBootTestを付ければいい

というわけではありません。

まず、

何を確認したいのか

を考える必要があります。

例えば、

Serviceの計算ロジックだけ確認したい
↓
JUnit + Mockito

ControllerのURLやViewを確認したい
↓
MockMvc + @WebMvcTest

Repositoryの検索処理を確認したい
↓
@DataJpaTest

複数の層を接続して確認したい
↓
結合テスト

というように、目的によってテスト方法を選びます。


まとめ

Spring Bootではテスト関連の仕組みが多いため、最初は混乱しやすいですが、

「どの範囲をテストしているのか」

で整理すると理解しやすくなります。

小さい

Service
↓
JUnit + Mockito

Controller
↓
@WebMvcTest + MockMvc

Repository
↓
@DataJpaTest

複数の層
↓
@SpringBootTestなど

E2E
↓
システム全体

大きい

JUnit、Mockito、MockMvcなどは同じ役割のツールではありません。

それぞれ、

JUnit = テストの土台

Mockito = 依存先をMockにする

MockMvc = Web層をHTTPに近い形でテストする

という役割があります。

まず「何をテストしたいのか」を決めてから、その範囲に合ったテスト方法を選択することが重要です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?