FactoryGirlとfixturesを比べてみる
ブログより書くべきものはあるのだけれど、バイオリズム的にそろそろブログ書きたいというか、インターン先の業務でFactoryGirlについて調べ直して以来、
どうしてもちょっと書きたい病に襲われていたのでFactoryGirlとfixturesについて書きます。
fixturesやFactoryGirlを利用するテストコードは、 Minitestです。
(Rails4.0のデフォルト設定です。直接名前はrails上に出てきませんがActiveSupport::TestCaseなどの中でincludeされています。参考: A Guide to Testing Rails Applications — Ruby on Rails Guides)
Fixturesについては
ActiveRecord::FixtureSet
FactoryGirlについては
factory_girl/GETTING_STARTED.md at master · thoughtbot/factory_girl · GitHub
他には
Rails testing: factories or fixtures?
を見ながら書いています。
お品書き
- test_helperへの設定
- アソシエーション
- 動的な値の生成
- まとめ
FactoryGirlはもっといっぱいできることがあるのですが、fixturesとの比較がやりたかったので、fixturesのできることにあわせました。
test_helperへの設定
FactoryGirl
と書くとFactoryGirl.create ... などで定義したfactory(基本的に1レコード分のテストデータ)を利用することができます。
factory(FactoryGirlを使うときの1レコード分のデータくらいの意味)は
のファイルにFactoryGirl.defineメソッドに与えるブロックの中に書きます。
fixtures
fixturesメソッドに、全部のymlを読み込みたい場合は:allをそうでない場合は、ymlのファイル名を
**.yml => :**
の形に直して読み込みたい分だけfixturesメソッドに与えましょう。こんな感じ。
ymlは基本的にkihonntekinitest/fixturesディレクトリにおきます。
テストごとに読み込みたいfixture(fixturesを使うときの1レコード分のデータくらいの意味)をテストファイルごとにyml単位で指定することができて、
その場合はテストファイル(というかクラス)ごとにfixturesメソッドを書きます。
ymlに書いたデータはテストのクラス内で以下のように呼び出せます。
放っておくと全部のデータがデータベースに登録されるので、
データベースに登録できない形式のデータがあったりするとはねられたり、不要なレコードがデータベースに保存されていたりするとなんか気持ち悪い、
という場合は、ActiveSupport::TestCaseのクラス内で
にしておくとfixtureの名前のハッシュだけ作成されてデータベースは綺麗です。
FactoryGirlの場合もそうなのですが、自動的にアソシエーション先のデータもデータベースに保存されている訳ではないので、
多対多のレコードのテストで、アソシエーション先のアソシエーション先の...とか考える場合は、
self.use_instantiated_fixtures = :no_instances
の使用や、FactoryGirlで行っている場合は注意が必要です。
FactoryGirlはたしか1個目のアソシエーションで指定しているレコードまでは作成してくれたと思います。
1個目のアソシエーションで指定しているレコードの中でアソシエーション先のレコードが指定してあるみたいな場合は
たしか、普通に書いているとレコード作ってくれなかったので、
initialize_withブロックの中でアソシエーション先のデータがあるかどうか確認して、
なければレコードを作成するみたいなことを書いてました。
それがいいかどうかとかは長くなりそうなのでこの記事ではふれません。
test factory antipatternとかでぐぐってください。
アソシエーション
一対多
FactoryGirl
fixtures
多対多
こっちはややこしいのでモデルの方も少し書きます
FactoryGirl
コールバックを使う書き方もあります。このへんの記事とかにも書いてます。
FactoryGirlのアソシエーションの使い方を確認する - woshidan's blog
fixtures
fixturesは多対多の場合かなりらくちんです。
正直これが書きたくてこの記事かきました。
動的な値の生成
FactoryGirl
FactoryGirlはsequenceだけとりあえず書いてますが、
ここに関して言うと、fixturesが静的なテストデータの扱いを目的としているので当たり前ですがFactoryGirlが圧倒的によいと思います。
fixtures
ymlにerbを使うことでできるといえばできます。
fixturesの方は正直読みたくない気がします。
まとめ
FactoryGirlとfixturesのどちらがいいか書くか期待されている気がしますが、場合によります。あなたのプロジェクトの詳細なんて知らないです(無責任)。
fixturesだとモデルと連携していないので値がぶっ壊れたら全部手動で修正です。
と書いてあることもあるんですが、FactoryGirlの場合もエラー出ますよね、壊れたデータ入っていたら。
これ、大変そうに見えるかどうか、何ですが、
テーブルの列名が変更された程度なら、置換使えばそれなりに対処できる気がします。
今時分置換使わないで開発している人なんていませんでしょうし。
最近の自分の趣味としては、FactoryGirlでもその場でfactoryの属性値を上書きしないようにしたいな(テストメソッドにはテストの流れのみ記述したいな)、
というのがありまして、
そうするとFactoryGirlもfixturesもそんなに使い心地がかわらないような気がします。
コントローラなどのテストでいくつかモデルのアソシエーションが入った固定のデータが欲しい、
という場合は、fixturesがよさそうだと印象を持ってます。
fixturesではIDが固定化できるし誤って同じレコードを複数作成する恐れも無いので、いくつかの実質定数が入っているテーブルがあって(たとえば都道府県など)、みたいな場合には非常に安心して使えます。
何より多対多のアソシエーションを使ってテストを行いたい場合が非常に簡単に書けます。
一方、マトリックス書いてモデルのメソッドの境界値条件のテストを簡潔に書きたい、
という要求があるならFactoryGirlのtraitや継承(せいぜい2~3階層くらい。それ以上階層やパラメータ増えると結局その場でActiveRecordのメソッドから作ってもあまりかわらない)がかなり便利だな、と思います。
どちらかしか使えない、みたいな流れがありますが併用して大丈夫です。
使っているとき、どのデータをどっちに書いたっけ、同じ名前のfixtureやfactoryがあるんだけど何が違ったっけ、とか
あるいは、fixtureとfactoryで重複する属性値のレコードを作成しちゃってバリデーションエラーをはくとか。
みたいな人間側の問題をうまくなんとかできれば。
関連して導入コストについていうと、
Railsの人はRailsチュートリアルから入る流れでFactoryGirlを使ったことがある、という前提があるので
それがなく、いまからまっさらな状態でどっちにしようかな、というのであれば導入コストはfixturesのほうが低い気がします。
もうみんながFactoryGirlで作っているなら、そのままのほうがたぶんいいです。
私個人ももうだいぶFactoryGirl覚えたし、今度新しいの一から作り出すまでは、FactoryGirlだけで開発してると思います。