自動テストにおいて、信頼性を上げるためにDBはモックではなく実際のDBを使ってテストをしたい。
その場合、データ管理はテストケースの都度
- データベース・テーブルを再作成(DROP/CREATE)する
- テーブルデータを再作成(DELETE/TRUNCATE/INSERT)する
- テスト開始前にトランザクションを開始し、テスト終了後にロールバックする
といった方法を取っていることが多いと思う。
この方式で 3 のトランザクション/ロールバック方式のデータ管理の採用を第一に検討すべきだよね、という話を書いていく。
理由1: 高速である
DROP/DELETE/TRUNCATE/CREATEは実行コストが高く、一般的には遅い操作となる。 インメモリDBを使う方法もあるが、言語やライブラリの実装に強く依存するし、結局マイグレーションにコストがかかりがちだ。
その点、トランザクション/ロールバック方式は一般的な高速な操作であり、テスト時間が増えがちなDBテストにおいては大きなメリットだ。
理由2: 並列性が高い
トランザクション分離レベルに依るが、ダーティリードでなければトランザクション内に作成されたデータは他のトランザクションから見ることができないので、トランザクション/ロールバック方式は並列実行がしやすい。
他の方式だと並列に実行する場合はデータベースを予め作成する必要があるし、並列数=データベース数となってしまう。
理由3: データのcleanupが簡単
トランザクション/ロールバック方式であればロールバックするだけでデータがリセットされる。そのためテストの安定性も上がりやすい。
他の方式だと、データベースやデータのcleanupを明示的に行う必要があり、やや面倒だし安定性も下がりがち。
各言語/フレームワークでは何を使うか
PHP/Laravelでは DatabaseTransactions トレイト
TypeScript/Prismaでは Quramy/jest-prisma
Goだと DATA-DOG/go-txdb
あたりを利用することになりそう。
ライブラリによってはトランザクションのネストに対応しておらず、結果としてアプリケーションのトランザクション処理に対応できないケースがあるので選定するときには注意が必要。