問題
Relation を繰り返し処理し、各メンバーでメソッドを呼び出す関数があります。
def do_stuff
count = 0
foo.bars.active.each do |bar|
bar.stuff
count += 1
end
count
end
注:は、配列ではなく関係を返すactive
スコープです。bars
私のテストは次のようになりました:
describe :do_stuff do
let(:foo) { FactoryGirl.create(:foo) }
before { foo.bars << FactoryGirl.create(:bar, :mock_stuff) }
subject { foo }
it { subject.do_stuff.should == 1 }
it "does lots of stuff" do
5.times { subject.bars << FactoryGirl.create(:bar, :mock_stuff) }
subject.do_stuff.should == 6
end
end
バーファクトリー
FactoryGirl.define do
data { random_string }
trait :mock_stuff do
after_build { |bar| bar.stub(:stuff).and_return(true) }
end
end
問題は、実際bar.stuff
に呼び出されることを確認していないことです。これにリファクタリングしようとしたとき、私は自分自身を燃やしdo_stuff
ました:
def do_stuff
foo.bars.active.count do |bar|
bar.stuff
end
end
count
ActiveRecord::Relation を呼び出してもブロックは実行されませんが、すべてのテストは引き続きパスします :(before
仕様に次のようなブロックが必要です:
before do
foo.bars.each do |bar|
bar.should_receive(:stuff)
end
end
問題は、bar
上記で返されたインスタンスが、コードでインスタンス化されたインスタンスとは異なることです。
答え
私はついにそれを理解しました。以下は、配列またはリレーションを反復処理しているかどうかを心配する必要があり、心配しないときに失敗する仕様です。
describe :do_stuff do
subject { FactoryGirl.create(:foo, :with_bar) }
it "does stuff to bar" do
Bar.any_instance.should_receive(:stuff)
subject.do_stuff
end
end
ここでの秘訣は、最初の例で行ったように、ブロック内で foo を定義できないことです。let
フーファクトリー:
FactoryGirl.define do
data { random_string }
trait :with_bar do
after_build do |foo|
foo.bars << FactoryGirl.create(:bar)
end
end
end
今、私が何か愚かなことをするfoo.bars.active.count do |bar|
と、スペックが失敗し、コードが壊れていることがわかります。