そこそこ複雑なデータを持たないとフィーチャテストを動かせないようなプロジェクトがありまして、きちんとしたデータを作るためにはCSVファイルからデータを取り込んでリレーション作るという必要がありました。
テストケースによってはこれを使わないこともありますが、全体の40%程度はこのデータがないとうまくいかないので、それぞれのテストの前に取り込み処理が入るります。これにより全体を流すのにめちゃくちゃ時間がかかっていました。
といっても数十分程度なので多少目をつぶってこれまではやり過ごしてきました。
このたび、重い腰をあげて対応策を考えましたので記しておきます。といっても↓の記事を大いに参考にして実施したので、こちらの記事に感謝します。
こちらの記事で紹介されているのはseed_dump gemを使ってDBのデータをactiverecord-import
の形式で書き出し、テストの必要な箇所で書き出したRubyのコードを実行するというとやり方です。大筋はこのやり方をとります。
細かいところで工夫した箇所があるのでその点について紹介します。
必要なデータだけのDBをつくる
元々CSVインポート処理で取り込まれていたデータはごく一部なので、開発用に使っているデータが全て必要なわけではありません。
そこで新しいデータベースを用意してテスト用のデータのみを取り込んだあとで、そのデータベースに対してseed_dump
により書き出せばよいと考えました。
ちなみに、テストコードでのインポート処理は、
MyTestData.import!
みたいに、ユーティリティクラスのメソッドを呼ぶだけとなっています。これを踏まえた上で、一時的なデータベースを作るために以下のようなことをしました。
- database.ymlを開き、developmentの
database
名をseed_tmp_dev
みたいな適当な名前に変更する rails db:create
などでデータベースを作成rails db:migrate
でマイグレーションrails console
でコンソールに入るMyTestData.import!
を実行
これで必要なデータだけが入った一時的なデータベースが完成しました。
seed_dump時の工夫
先程紹介した記事ではRubyスクリプトによりデータを出力していました。seed_dump
にはrakeタスクがありますので、今回はそちらを使いました。データベースにあるデータを全て書き出してくれればいいので、モデルの絞り込みは特にしませんでした。
出力用のコマンドは次のようになりました。
% rake db:seed:dump EXCLUDE=created_at,updated_at IMPORT=true FILE=spec/fixtures/seed.rb
記事と同様に以下のオプションを指定しています。
EXCLUDE
除外カラム。関連のためidは出力対象です。IMPORT
activerecord-import対応の形式で書き出します。FILE
出力先のファイルパスを指定しています。
さて、このファイルを元記事のように:seed
タグ有りのときに読み込むようにして実施したところ、関連の外部キーでエラーになりました。
出力されたファイルを見てみると、アルファベット順に処理が書かれています。
# 例
Child.import(...)
Parent.import(...)
ここでChildが外部キーparent_id
あたりでParentを参照していたりするとChildをimportするときにエラーとなります。本来は以下のような順番で取り込まれるべきです。
# こうなっているべき
Parent.import(...)
Child.import(...)
今回のプロジェクトの場合、関連するモデルが15程度あったのでそれぞれ親子関係をたどって出力されたファイルのコードを並び替える必要がありそうでした。
ここでseed_dump
では対象のモデルを指定するやり方があることを思い出しました。rakeタスクの場合はMODELS="Parent, Child"
のように指定可能です。
% rake db:seed:dump MODELS="Parent, Child" EXCLUDE=created_at,updated_at IMPORT=true FILE=spec/fixtures/seed.rb
これを実行したところMODELS
に指定した順番でコードが出力されており、望み通りにインポートできそうなところまでいきました。
バリデーションの回避
一部のモデルではデータベースのカラムの情報だけでは保存できないようなバリデーションがかかっているモデルがありました。
そのため、出力されたファイルのParent.import(\[...\])
のところにvalidate: false
オプションをつける必要がありました。
# before
Parent.import([...])
# after
Parent.import([...], validate: false)
この変更はエディタのマルチカーソル機能でがんばって変更しましたw
ここまででようやく取り込めるようになりました。
結果
対象のテストを流してみたらこんな結果となりました。
変更前
Finished in 41 minutes 15 seconds (files took 11.15 seconds to load)
変更後
Finished in 17 minutes 25 seconds (files took 11.13 seconds to load)
41分→17分ということで、40%程度に短縮できました!