Rspec"假设接受"

时间:2016-11-04 17:41:42

标签: ruby-on-rails ruby rspec

我意识到我编写测试的方式正在产生误报。

说我有这个源代码

class MyClass
  def foo
  end
  def bar
    1
  end
end

foo方法什么都不做,但是我想写一个测试,确保它在引擎盖下调用bar(即使它没有)。此外,我希望确保直接调用bar的结果为1

it "test" do
  inst = MyClass.new
  expect(inst).to receive(:bar).and_call_original
  inst.foo
  expect(inst.bar).to eq(1)
end

所以这是真的,但我希望它返回false。

我想要这一行:

  expect(inst).to receive(:bar).and_call_original

考虑到在我的测试用例中我直接调用inst.bar这一事实。我希望查看foo方法的内部。

2 个答案:

答案 0 :(得分:1)

您在一个测试用例中定义了2个单独的测试用例。您应该将其更改为2个单独的测试。

describe '#bar' do
  it "uses #foo" do
    inst = MyClass.new
    allow(inst).to receive(:foo).and_call_original
    inst.bar
    expect(inst).to have_received(:foo)
  end

  it "returns 1" do
    inst = MyClass.new
    # if you don't need to mock it, don't do it
    # allow(inst).to receive(:foo).and_call_original
    expect(inst.bar).to eq(1)
  end

  # if you really, really wan't to do it your way, you can specify the amount of calls
  it "test" do
    inst = MyClass.new
    allow(inst).to receive(:foo).and_call_original
    inst.foo
    expect(inst.bar).to eq(1)
    expect(inst).to have_received(:foo).twice # or replace .twice with .at_least(2).times
  end
end

答案 1 :(得分:1)

存根通常以两种方式使用:

  1. 检查方法是否被调用,即expect_any_instance_of(MyClass).to receive(:foo)在这种情况下它返回的内容并不是真正重要的
  2. 模拟行为allow_any_instance_of(MyClass).to receive(:method).and_return(fake_response)。这是避免数据库交互和隔离测试中其他依赖项的好方法。
  3. 例如,在需要对Rails ActiveRecord模型Product进行数据设置的测试中,该模型具有多个关联comments

    let(:product) { Product.new }
    let(:comments) { [Comment.new(text: "Foo"), Comment.new(text: "Bar")] }
    
    before :each do
      allow_any_instnace_of(Product).to recieve(:comments).and_return(comments)
    

    现在,当您拨打it时,在任何product.comments区块中,您将获得一系列评论,您可以在测试中使用这些评论而不必靠近您的数据库,从而使测试顺序的速度更快

    当您使用存根来检查方法是否被调用时,关键是在执行调用方法的opreation之前声明期望值。例如:

    expect_any_instance_of(Foo).to recieve(:bar).exactly(1).times.with('hello')
    Foo.new.bar('hello') # will return true