前回、単体テストによって、個々のクラス(ServiceやController)のロジックが正しいことを、モックを使って検証する方法を学びました。
しかし、単体テストだけでは以下の重要な点は検証できません。
- DIコンテナの動作:
@Autowiredや@Componentが正しく設定され、**SpringのDI(依存性注入)**が意図した通りに行われているか。 - 層間の連携: ControllerからService、ServiceからRepositoryへのデータ受け渡しが正しく機能しているか。
- 永続化:
@Transactionalや JPA のマッピングが正しく、実際にデータベースにデータが書き込まれているか。
今回は、これらの複数の要素を結合して検証する**結合テスト(Integration Test)**の書き方を学びます。
1. 結合テストとは?
結合テストは、アプリケーションを構成する複数のコンポーネントや層を組み合わせて、エンドツーエンドに近い動作を検証するテストです。
- 特徴: Spring BootのDIコンテナを実際に起動し、本物に近い環境でテストを実行します。
- 目的: 開発者が記述したビジネスロジックだけでなく、Spring Frameworkが提供するフレームワーク機能(DI、トランザクション、MVCマッピングなど)の動作保証を主な目的とします。
2. Spring Boot Testの基本的な構成
結合テストを行うには、Spring Bootのテスト機能を使います。
2-1. @SpringBootTest アノテーション
テストクラスに @SpringBootTest アノテーションを付与すると、Spring Bootアプリケーションの起動に必要なすべての設定(@SpringBootApplication など)を読み込み、DIコンテナを起動します。
Java
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles; // テスト用プロファイルの使用
@SpringBootTest
@ActiveProfiles("test") // テスト用の設定ファイル(application-test.ymlなど)を使用
class ItemServiceIntegrationTest {
// 起動したコンテナから本物のServiceを注入する
@Autowired
private ItemService itemService;
// ... Repositoryも注入して、テスト後のデータを確認することも可能
@Autowired
private ItemRepository itemRepository;
// ... テストメソッド
}
このとき、@Autowired で注入される itemService は、本物の Service クラスのインスタンスであり、その中に注入されている itemRepository も本物の Repository のインスタンスです。
2-2. テスト環境の分離(テストプロファイル)
結合テストではDBアクセスが発生するため、開発用や本番用のDBを汚染しないよう、テスト専用のDBを使うのが鉄則です。
- テストプロファイル:
application-test.ymlなどの設定ファイルを用意し、テスト専用のH2データベース(インメモリDB)などの設定を記述します。 @ActiveProfiles("test")を付けることで、このテスト実行時のみテストプロファイルが適用されます。
3. トランザクションとロールバック
結合テストでデータを作成・更新した後、そのテストが終わるたびにDBからデータを削除する作業は手間がかかります。
Spring Bootの結合テストでは、@Transactional アノテーションをテストメソッドに付与することで、この手間を解消できます。
Java
import org.springframework.transaction.annotation.Transactional;
@SpringBootTest
class ItemServiceIntegrationTest {
// ... @Autowired で Service を注入
@Test
@Transactional // テストメソッドの実行後に、自動でDB変更をロールバック(取り消し)する
void testItemCreationAndFind() {
// 1. データの作成(DBに書き込まれる)
Long newId = itemService.createItem("テスト商品", 100);
// 2. データの検証(DBから読み取る)
Item createdItem = itemRepository.findById(newId).orElseThrow();
assertEquals("テスト商品", createdItem.getName());
// 3. メソッド終了後、@Transactional により、作成したデータはDBから自動で削除される(ロールバック)
}
}
テストクラスやメソッドに @Transactional を付けると、テスト終了時に全てのDB操作が自動でロールバックされ、次のテストに影響を与えないクリーンな状態を保てます。
4. Controller層のテスト(Web層の検証)
Webアプリケーションの結合テストで最も頻繁に行われるのが、Controller層の検証です。外部からHTTPリクエストを送信し、正しいレスポンス(HTTPステータスコード、JSONボディなど)が返ってくるかを検証します。
これには、Spring Bootが提供する MockMvc クラスを使用します。
Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@SpringBootTest
@AutoConfigureMockMvc // MockMvcを自動設定し、DI可能にする
class ItemControllerIntegrationTest {
@Autowired
private MockMvc mockMvc; // サーバーを起動せずにHTTPリクエストをシミュレーションする
@Test
void testGetItem_Success() throws Exception {
// 1. HTTP GETリクエストをシミュレーション
mockMvc.perform(get("/api/items/1"))
// 2. 検証 (HTTPステータスコードが200 OKであること)
.andExpect(status().isOk())
// 3. 検証 (レスポンスボディのJSONに含まれる name フィールドが期待値であること)
.andExpect(jsonPath("$.name").value("既存の商品名"));
}
}
MockMvc は、実際にTomcatなどのWebサーバーを起動することなく、Spring MVCの処理パイプラインだけを通してリクエストを処理するため、非常に高速なWeb層のテストが可能です。
✅ 本日のまとめ
- 結合テストは、
@SpringBootTestでDIコンテナを起動し、複数層(Controller, Service, Repository)の連携とフレームワークの動作を検証する。 - テスト専用のDBを使用するため、
@ActiveProfiles("test")でテストプロファイルを適用する。 - テストメソッドに
@Transactionalを付与することで、テスト実行後にDBの変更が自動でロールバックされ、テスト環境がクリーンに保たれる。 - Controller層のテストには、WebサーバーなしでHTTPリクエストをシミュレーションできる
MockMvcを使用する。
🔔 次回予告
単体テストと結合テストの知識が揃ったことで、堅牢なアプリケーション開発の基盤が整いました。
次回からは、アプリケーションのデプロイ(配置)と運用に関する実践的なテーマに進みます。次回は、構築したSpring Bootアプリケーションをコンテナ化するための Docker の基本と、Dockerfileの書き方を学びます。
次回:【第41回】アプリケーションのコンテナ化!〜Dockerの基本とDockerfile〜 にご期待ください!


コメント