我是使用RSpec和FactoryBot进行测试的新手,所以我们将不胜感激。使用以下代码/测试时,我遇到了一个奇怪的情况。
这是我的模特:
class Foo < ActiveRecord::Base
has_many :bars, dependent: :destroy
def update_baz_count(baz_count)
most_recent_bar.update_current_baz_count(baz_count)
end
def most_recent_bar
bars.last
end
end
class Bar < ActiveRecord::Base
belongs_to :foo
def update_current_baz_count(new_baz_count)
self.baz_count = new_baz_count
self.save
end
end
这是我的测试:
describe Foo do
# This test passes
describe "#most_recent_bar" do
let!(:foo) { create(:foo) }
let!(:bar) { create(:bar, foo: foo) }
it 'should return the most recent bar' do
expect(foo.most_recent_bar).to eq(bar)
end
end
describe '#update_baz_count' do
let!(:foo) { create(:foo) }
let!(:bar) { create(:bar, foo: foo) }
it 'should call update_current_bar_count on the storage history' do
## Test will NOT pass unless following line is uncommented:
# expect(foo).to receive(:most_recent_bar).and_return(bar)
expect(bar).to receive(:update_current_baz_count).with(1)
foo.update_baz_count(1)
end
end
end
问题在于,在我的#update_baz_count
测试中,通过测试取决于对#most_recent_bar
方法的期望。如上所述,我对#most_recent_bar
的测试通过了,因此在其专用测试之外对该方法的性能进行断言是多余的。
那么,为什么成功取决于行expect(foo).to receive(:most_recent_bar).and_return(bar)
?
答案 0 :(得分:1)
问题是您在规范中可用的对象上设置了模拟行为:
expect(bar).to receive(:update_current_baz_count).with(1)
但是!在生产代码中,将从db获取同一行:
bars.last
然后,AR将为您创建一个新对象,但不知道您已在规范中对其进行了嘲笑。
您可以像这样检查它:
expect(bar.object_id).to eq foo.most_recent_bar.object_id
哪个会失败。
如果您想在不嘲笑的情况下进行操作,请执行以下操作:
it 'should update update_current_bar_count on the storage history' do
expect{ foo.update_baz_count(1) }
.to change { bar.reload.field_that_baz_count_updates}.from(0).to(1)
end
因此,不必检查方法是否已被调用,而应检查方法的调用对“世界”的影响。