如何测试在ActiveRecord :: Relation的每个元素上调用方法?

时间:2013-10-02 21:08:23

标签: ruby-on-rails activerecord rspec relation

问题

我有一个通过Relation迭代的函数,并在每个成员上调用一个方法:

def do_stuff
  count = 0
  foo.bars.active.each do |bar|
    bar.stuff
    count += 1
  end
  count
end

注意:activebars上的范围,它返回Relation而不是Array。

我的测试看起来像这样:

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

即使在ActiveRecord :: Relation上调用count也不会执行块,所有测试仍然通过:(我想在我的规范中有一个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

这里的诀窍是你不能let块中定义foo,就像我在第一个例子中所做的那样。

The Foo Factory:

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|这样愚蠢的事情时,我的规范失败了,我知道代码已经坏了。

1 个答案:

答案 0 :(得分:0)

我会做这样的事情:

let(:foo) { FactoryGirl.create(:foo) }
let(:bar) { double }
let(:array) { [bar] }

before do
  foo.stub(:bars).and_return(array)
end

it 'calls stuff on bar' do
  expect(bar).to receive(:stuff).once
  foo.do_stuff
end

您实际上不需要确保它将被调用foo.bars.count次,我们可以假设ruby count方法正常工作。如果它被调用一次,它也将被称为n次。