Rails のテストを書いていて、フィクスチャはのちのちメンテナンスに苦労しそうだから FactoryGirl を使ってみた。
FactoryGirl についてはここが詳しい http://www.func09.com/wordpress/archives/532
上のページの例にあるとおり、中間テーブルを使うようなケースでテストデータを定義するのに便利らしい (公式ページ?でもそのように宣伝していた) のだが、中間テーブルが不要な has_many/belongs_to の関係を定義するときに、 association を親子どちらに書くかでハマってしまった。
すぐ動かせるコード例がなくて恐縮だけど、 FactoryGirl で has_many/belongs_to な association を定義するときは、必ず子のほうに親の Factory を書かないといけないっぽい。このスレッド参照。 Google グループ
はしょったサンプルコードを書くと、こんな感じ:
### models ### # app/models/user.rb class User < ActiveRecord::Base has_many :bookmarks end # app/models/bookmark.rb class Bookmark < ActiveRecord::Base belongs_to :user end ### wrong case ### # test/factories/user.rb Factory.define(:sasimi), :class => User do |u| u.name "Kensuke Kaneko" u.bookmarks { [Factory.create(:30daysalbum), Factory.create(:ficia)] # ココがダメ } end # test/factories/bookmark.rb Factory.define (:30daysalbum), :class => Bookmark do |b| b.url 'http://30d.jp/', end Factory.define (:ficia), :class => Bookmark do |b| b.url 'http://ficia.com/', end ### test code ### class UserTest < Test::Unit::TestCase def test_sasimi_has_2_bookmarks sasimi = Factory.create(:sasimi) # Factory.create する途中でエラー assert_equal 2, sasimi.bookmarks.size end end ### right case ### # test/factories/user.rb Factory.define(:sasimi), :class => User do |u| u.name "Kensuke Kaneko" end # test/factories/bookmark.rb Factory.define (:30daysalbum), :class => Bookmark do |b| b.url 'http://30d.jp/', b.user { Factory.create(:sasimi) } # こっちに書かないといけない end Factory.define (:ficia), :class => Bookmark do |b| b.url 'http://ficia.com/', b.user { Factory.create(:sasimi) } # ...でもコレは別の理由でエラーになることも end ### test code ### class UserTest < Test::Unit::TestCase def test_sasimi_has_2_bookmarks 30daysalbum = Factory.create(:30daysalbum) ficia = Factory.create(:ficia) # なんか自分で書いてて間違ってる気がしてきましたが...子のほうから Factory.create してやらないといけない sasimi = Factory.create(:sasimi) assert_equal 2, sasimi.bookmarks.size end end
File: README — Documentation for thoughtbot/factory_girl (master) を読んでみても、中間テーブルを利用するケースは例があるのだけど、ふつうの has_many/belongs_to の場合については記述がなかった(とおもう)。これは不親切だと思う。
あと、上のコード例のなかにコメントで書いたけど、 User モデルに validates_uniqueness_of :name があると、 30daysalbum と ficia を Factory.create するときに同じ user インスタンスを二度 create してしまうので、バリデーションエラーになる。これはまぁ当然の話だし回避策もいちおうあって、 sequence というのを使うとユニークな値を持った Factory を簡単に定義できる。
Factory.sequence(:yappo) do |n| "yap" + "p" * n + "o" end Factory.next(:yappo) #=> yappo Factory.next(:yappo) #=> yapppo
なんだけど、これだと user のインスタンスがそれぞれ別のものになってしまう。同じユーザーが複数のブックマークを持ってる、という状態をつくりたいときは(has_manyなんだからそうしたいはず) bookmark のインスタンスを作るときに user のインスタンスを渡してやらないといけない。
user = Factory.create(:sasimi) 30daysalbum = Factory.create(:30daysalbum, :user => sasimi) ficia = Factory.create(:ficia, :user => sasimi)
これはちょっと面倒くさい。
そんなわけで、 FactoryGirl を使ってみた。感想としては、フィクスチャよりは把握しやすいかな?という気がした。べつに YAML で書いておくメリットも特に思いつかないし、だったら Ruby で書いてあってもいいよねと。ただまぁやりすぎると結局ふつうにテストコードの中で User.create とかしてるのとあんま違いがないのでは、という風になっていってしまうのかな。あと上に書いたように少し面倒なところがある。まぁこの例だったらフィクスチャでよくね?って話なのかもしれない。 FactoryGirl よりも Machinist のほうがよさげという話もどこかで見かけたので、そちらも試して比較してみたいなと思う。