ユニットテストのDB運用基礎

途中から参画したPJで開発しているAPIリポジトリで、UTを実行したところ、テーブルのレコードを書き換えてしまうという事件が起きた。

PJではサービス層のUTしかコーディングしていないが、なんでDBに接続しているの?を発端に、UTでDBってどのように考えるものなのか調べてみた。

サービス層のテストでDB接続は不要

調べた結果、今回テーブルを書き換えたAPIは、一部サービスのみでなくリポジトリのテストも実装しており、そのテストコードがメンテされておらず起こったエラーだった。

サービス層のテストは基本的にMockで実装するため、ここだけをテストするのであれば、UTにDB接続は一切不要であり、ベストプラクティスとなる。

リポジトリのテストをする場合は必要

僕のPJではテーブル構成がクライアントから共有されてくるため、ほとんどリポジトリ層のテストを実施していなかった。

そもそもリポジトリ層は、単純なCRUDならテストは書かないのが一般的。ただし、以下の場合はリポジトリ層のテストが推奨される。

  • 独自かつ複雑なクエリを作成した
  • サービスにDBデータ依存の重要なロジックがある
  • DB依存のバグを防ぎたい(DBの挙動自体をテストしたい)

リポジトリ層のテスト

リポジトリ層のUTをどのように実行するか、主に2つの方法がある。

  • インメモリDBを使用する
  • 実際の開発用DBを使用する

インメモリDBを使用する場合

H2などのインメモリDBを使用するのが一般的。テーブルは、DTOから自動生成する。

この構成は以下の特徴があり、小規模開発向き。

  • 仮想DBなので、実DBのデータを更新するリスクが無い
  • DTOを@Entityで管理必須。逆に言えば、@Entityを使用していなければ、インメモリDBは使用していない
  • 本番用のテーブルもDTOから自動生成できるので扱いがとてもシンプル
  • @Entityは、インデックスや制約に限界があるため、複雑なDBを構築できない

application.ymlで以下を設定するだけで、UTでは勝手にインメモリDBを使用してくれる。

spring.jpa.hibernate.ddl-auto=create

このときUTでは@Entityで作成されたDB、本番環境ではDDLで作成されたDBに対してアクセスする。

そのため、正しいDBでテストしているという保証ができないため、HibernateのSchema Validationなどで、整合性を担保する必要がある。

実際の開発用DBを使用する場合

実DBに向けてテストする。

  • DDLとマイグレーションツール(Flyway, MyBatis)でDB管理
  • @Transactionalを正しく使用すれば、DBが更新されることは無いはず。
  • リスクは0ではないため、CIでは、UT用スキーマを作成し使用すること推奨。
  • ローカルからIT(結合テスト)用のDBにつないだ状態でUT実施しないこと。

マイグレーションツールについて簡単に解説。

  • Flyway:マイグレーションツール。DDLからDB構築
  • MyBatis Generator: DDLからDTO/Mapper生成
  • MyBatis:SQLとJavaオブジェクトのマッピング(xml)

まとめ

  • 小規模開発:DTOを@Entityでお手軽管理、UTではインメモリDBを使用
  • 大規模開発:DDLとマイグレーションツールでDB管理。CIでは、UT用スキーマを用意して使いましょう。

分岐点は、複雑なDB制約があるかどうかになってくる。

コメント

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