自動テストは開発、保守であると助かる機能です。人間の手でやっていては日が暮れるような量のテストも待ってる間にやってくれるため、影響範囲が広範であったり予想できなかったりする変更も安心してできます。とはいえテストを書くのがしんどい時もあります。例えば複雑なテストデータが必要な時です。これは結合テストや具体的な原因が不明ながらもDB(データベース)が特定の状態の時だけ異常な動作をすることがわかっている時の修正用のテストで必要になりがちです。これを比較的楽に作るための方法を紹介します。
方針は複雑なテストデータが必要なテストを行う度にテスト用のデータをDBのダンプからまるっと作り直す、というものです。手書きで複雑なデータを書くのは面倒ですし、他のテストのことも考慮したテストデータを作るのも面倒です。そこでテストデータをアプリケーション上の操作を用いて作り、それをダンプし、テストデータをそのダンプから都度作り直します。この方法を素朴に行う場合の問題点はテストデータの生成時間の影響でテスト時間が非常に長くなるという点にあります。これを軽減するために次の手順でテストデータを作ります。
-
状況の再現
最初に実際のアプリケーション操作を通じてテストしたい状態を手動で再現します。これはテストの際に使うデータを作り出すことが目的です。
-
データベースのダンプ
mysqldump等を用いて目的の状態でDBのダンプを取得します。このダンプは次の工程で使います。
-
データを小さくする
テストの実行時間を短くするためにテストに関連しないデータを削除します。
TRUNCATE
、DELETE
を利用して関係ない部分をガンガン削除します。小さいデータになればまるっとDBを作り直しても許容範囲の時間に収まります。過度に削除してしまった場合は2で作ったダンプでDBを復元して、次は間違えないようにまた削除を始めます。 -
最終的なダンプの作成
満足いく程度に不要なデータを削除した後にDBをダンプします。このダンプをテストの度に使います。
ダンプができたら次のようなコードでテストをします。
<?php namespace Tests\Integration; use Carbon\Carbon; use Tests\TestCase; class FixMissingCompanyTest extends TestCase { public function test会社が消える問題のテスト(): void { // データベースの初期化とダンプデータの読み込み $start = microtime(true); \Artisan::call('db:wipe'); // DBをまっさらにする \DB::unprepared(file_get_contents(__DIR__.'/20240419_142023.sql')); // ダンプした内容をDBに流し込む // いずれプログラムはダンプした時と異なるDB構造で動くのが前提になるので、 // DB構造のキャッチアップのためにマイグレーションを実行する \Artisan::call('migrate'); $end = microtime(true); // データベース再現にかかった時間を出力。あまりに長かったらまたDBを削る dump('データベースの再現にかかった時間: '.($end - $start).'秒'); // テストの本体をここに記述 } }
Laravel組み込みのコマンドでDBをまっさらにし、ダンプしたSQLを直接実行し、DB構造をソースコードに合わせる、といった処理です。この処理で目的の状態を再現できます。テストを実行する環境に既にあるデータを壊したくない場合は phpunit.xml に次のように書くなどしてテスト用のスキーマを使うように指定すると便利です。
<?xml version="1.0" encoding="UTF-8"?> <phpunit> <!-- ここに色々他の設定 --> <php> <!-- ここに色々他の設定 --> <env name="DB_DATABASE" value="test_db"/> </php> </phpunit>
複雑なデータを元にしたテストは手作業でテストする場合でも手順が増えがちで繰り返し行うのはとても面倒なので、多少手間をかけても自動化した方が楽です。