以前の記事で、DBに関連するテストにおいてはRefreshdatabaseではなくDatabaseTransactionsを使用していることを紹介しました。
先に実行したテストケースの結果が後続のテストケースに影響を与えないようRefreshdatabaseかDatabaseTransactionsのいずれかは使用すべきだと思いますが、Refreshdatabaseはテストケースごとに「migrate:refresh」が実行されるためその分時間を要しますし、テストケースによってはある程度まとまった量のデータを登録した上で行いたいようなものもあり、実行単位で最初からデータを登録しなければならないのはかなり非効率だったため、DatabaseTransactions(テストケースごとにトランザクション化し、最後にロールバックする方式)の方を採用しました。
言い換えれば、既存データを利用しつつ、テストの実行結果として生じたDBの変更は破棄される(実行前の状態に戻る)と言う形にしたかった訳です。
DBに関してのみで言えばDatabaseTransactionsを使用することで上記目論見は達成できました。
しかし、Storageが絡んできて事情が変わりました。
例えば、商品データの登録を考えてみます。
商品データの中には商品画像なども含まれているとします。
このデータの登録結果として、DB上の商品テーブルに新たなレコードが追加されますが、同時に関連する画像ファイルをStorageに保持したくなります。この動作をテストした場合に問題が生じます。
DatabaseTransactionsを使用することにより、追加されたレコードはテスト終了時に削除されますが、Storageに登録した画像ファイルは残ってしまいます。登録に関してはまだ不要なファイルが残るだけなので良いですが、削除に関してはレコードは復元されても対応するファイルは存在しなくなっていると言う矛盾が発生してしまうことになります。
テスト時にStorageに登録したデータが邪魔だと言うことであれば、「Storage::fake()」を使用すれば、登録したデータが残らなくなります。ただし、この機能は対象となる環境(例えばpublic)を引数に指定することで、使用するStorage環境を通常の「storage/app」ではなく「storage/framework/testing/disks」に切り替えつつ、同配下に指定した名称のフォルダを作成して使用し、かつ同メソッド実行時に当該フォルダごと削除して再作成すると言うようなことをしています。
ロールバックのように使用し終わった際に更新内容を消すのではなく、使用する前に古いデータを消すと言う辺りもDBの事情とは違っていますが、いずれにしても既存データを流用すると言うことはできない訳です。
また上記メソッドの発展形で「Storage::persistentFake()」なる機能もあり、こちらは格納先として「storage/framework/testing/disks」を使用しつつもファイルの削除は行わないようですが、それではテストによって発生したファイル環境の変更結果がそのまま残ることになるため、ロールバックにより元に戻されたレコードとファイルの状態にやはり不整合が生じてしまうことになります。
上記のような事情から、DBとファイル環境の整合性と言う意味では以下のいずれかの方法を採用するしかないと言うのが現在の認識です。
- RefreshdatabaseやDatabaseTransactionsは行わず、ファイル環境に関しても特に何もしない。
つまりはテストによって発生したレコードおよびファイルの変更内容は残り続けるが整合性はとれている。 - DBに関してはRefreshdatabaseを使用し、ファイル環境に関してはStorage::fake()を使用する。
つまりはテストごとに新規に必要なレコード、ファイルを用意し、共に既存データには一切依存しないと言う形で整合性がとれている。
経験上、前者のような形ではテストに際してのレコードやファイルの状態が期待された形になっていないケースが生じる場合があり、既存データの流用と言う前提自体が破綻する(少なくとも既存データを完全に信頼できない)と言うことは分かっています。
となると、結局はRefreshdatabase+Storage::fake()でテスト環境を常に初期化+再構築し続ける形が最適と言うことになりますが、最初に問題視したようにこれだとテスト効率が悪いんですよね…
そもそもロールバックにより実行前の状態に戻せるDBとは異なり、ファイル環境を元に戻すことは難しいとは思います。新規登録したファイルに関しては削除すれば良いですが、上書きしたり削除したりする操作に関しては元ファイルをバックアップしておいてテスト終了時に復元する必要があります。
その辺を自動でよしなにやってくれるよう期待することは少々贅沢かもしれませんが、何とかならないですかね、ファイル環境のロールバック(他力本願)。