RSpec:除非还测试了其中调用的方法,否则方法测试将不会通过?

时间:2018-06-21 16:57:10

标签: ruby-on-rails rspec tdd rspec-rails

我是使用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)

1 个答案:

答案 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

因此,不必检查方法是否已被调用,而应检查方法的调用对“世界”的影响。