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" 的影响。