長いな。うまくかみ砕けてない証拠ですね。
GitHub みたいなものを作ってるんだと思ってください: こういうモデルがあるとする。有料課金していないユーザーは非公開レポジトリを1個までしか作れない。
class User < ActiveRecord::Base has_many :repositories def paid? self.payment_flag end end class Repository < ActiveRecord::Base belongs_to :user def before_create unless user.paid? if self.publicity == false && user.repositories.count(:conditions => ['publicity = ?', false]) > 0 return false end end end end
これに対するテストを書く: すいませんソラで書いてて実際動かしてないので間違ってるかもしれないです。
class RepositoryTest < Test::Unit::TestCase def setup Factory.define(:user) do |user| end Factory.define(:paid_user), :class => User do |user| user.payment_flag :true end Factory.define(:repository) do |repository| repository.user { Factory.create(:user) } repository.publicity true end Factory.define(:private_repository), :class => Repository do |repository| repository.user { Factory.create(:user) } repository.publicity false end end def test_create_private_repository private_repository = Factory.create(:private_repository) assert_kind_of Repository, private_repository other_private_repository = Factory.create(:private_repository) assert_nil other_private_repository # fail end end
最後のアサーションは、「無料プランのユーザーは非公開レポジトリを複数持てない」ということをテストしたくて書いたのだけど失敗する。なぜなら private_repository と other_private_repository はそれぞれ別の user インスタンスを参照しているから。 Factory.define のなかで遅延評価で親となるリレーションを記述してしまうと、共通の親を持たせることができない。で、結局こんな風にしてしまう:
def test_create_private_repository user = Factory.create(:user) private_repository = Factory.create(:private_repository, :user => user) # inject user instance manually... assert_kind_of Repository, private_repository other_private_repository = Factory.create(:private_repository, :user => user) # ...and inject same user instance manually, again. NOT DRY :( assert_nil other_private_repository end
Factory.create(:private_repository, :user => user) ってところ、 :user => user を毎回書くのはだるいしかっこ悪いですよね、ていうかこうやって毎度毎度自分でオブジェクトの親子関係を考慮して初期データを注入してあげなきゃいけないんじゃ fixture replacement を使うメリットがあんまりなくね?それならふつうの fixture を頑張って真面目に書いたほうが Factory.create を何度も書く必要もなくなってより DRY なんじゃね?
...というのが嫌だよね、という話だったと理解しています。結局ここは machinist (は最近開発が滞ってるらしいけど) でも fabrication でも変わらず、やっぱり難しいというか、難解ではないし解決不能でもないけどスマートなやり方はなくて悩ましいね、というのが今のところの結論だったように思う。