RSpec: 方法测试不通过,除非其中调用的方法也被测试?
RSpec: Method test will not pass unless method called therein is also tested?
我刚开始使用 RSpec 和 FactoryBot 进行测试,如有任何帮助,我们将不胜感激。我遇到了以下 code/tests.
的奇怪情况
这是我的模型:
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)
?
问题是您在规范中可用的对象上设置了模拟行为:
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
因此,与其检查方法是否被调用,不如检查方法调用对 "world" 的影响。
我刚开始使用 RSpec 和 FactoryBot 进行测试,如有任何帮助,我们将不胜感激。我遇到了以下 code/tests.
的奇怪情况这是我的模型:
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)
?
问题是您在规范中可用的对象上设置了模拟行为:
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
因此,与其检查方法是否被调用,不如检查方法调用对 "world" 的影响。