途中から参画した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制約があるかどうかになってくる。

コメント