无法弄清楚这个rspec错误

时间:2014-01-08 08:44:01

标签: ruby-on-rails rspec

我正在测试缓存过期,在创建新的ProjectInfo时,项目和ProjectInfos的缓存都应该过期。我有以下测试:

    it "creating a new project info should invalidate the cache" do
      2.times { FactoryGirl.create(:project_info) }
      ProjectInfo.should_receive(:all).and_call_original
      Project.should_receive(:all).and_call_original
      Project.fetch_all

      Project.should_not_receive(:all)
      Project.fetch_all
      ProjectInfo.should_not_receive(:all)
      ProjectInfo.fetch_all

      FactoryGirl.create(:project_info)
      Project.should_receive(:all).and_call_original
      Project.fetch_all <- problem line
      ProjectInfo.should_receive(:all).and_call_original
      ProjectInfo.fetch_all
    end

我收到以下错误:

 1) ProjectInfo Caching creating a new project should invalidate the cache
     Failure/Error: Project.fetch_all

 Project(id:integer, name:string).all({:include => :project_info})
 expected: 0 times with any arguments
   received: 1 time with arguments: ({:include=>:project_info})

如果最后一个Project.fetch_all有问题的行。为什么rspec期待它0次?

2 个答案:

答案 0 :(得分:1)

<强>摘要

在同一示例中对同一对象设置否定期望后,无法为对象设置正期望值。

<强>详细

我不知道RSpec的行为是在对已经设定了预期的事物设定期望时正式定义的。我制作了一组测试,其结果如下所示,试图提出一个统一的模型。

从概念上讲,似乎RSpec似乎将每个对象#方法的期望放入一个特定于对象#方法的FIFO队列中,然后在每个方法调用和块结束时检查对该队列的期望。如果“满足”对队列中的条目的期望,则期望递减或移除(如果递减变为0)。如果没有达到预期,那么期望就会失败。

鉴于这种情况,如果调用该方法,则在“should_not_receive”之后放置“should_receive”将始终失败,如您的情况。

此问题的解决方案和RSpec预期的方法是将您的单个示例分解为多个示例,这样您就不会在同一个示例中对同一个对象#方法对设置两次期望。

请注意,在下面的示例中,我在RSpec报告错误的行上显示错误。在不期望的调用的情况下,这将是调用的行。如果设置了调用的期望但是块的结尾没有满足,那将是设置期望的行。

class Foo; end

describe "multiple expectations on same object with single it clause" do
  it "yes, doit" do # succeeds
    Foo.should_receive(:bar)
    Foo.bar
  end
  it "no, yes, doit" do 
    Foo.should_not_receive(:bar)
    Foo.should_receive(:bar)
    Foo.bar # fails, expected 0, received 1
  end
  it "yes, doit, no" do # succeeds
    Foo.should_receive(:bar)
    Foo.bar
    Foo.should_not_receive(:bar)
  end
  it "yes, doit, yes, doit" do # succeeds
    Foo.should_receive(:bar)
    Foo.bar
    Foo.should_receive(:bar)
    Foo.bar
  end
  it "yes, yes, doit, doit" do # succeeds
    Foo.should_receive(:bar)
    Foo.should_receive(:bar)
    Foo.bar
    Foo.bar
  end
  it "yes, yes, doit" do
    Foo.should_receive(:bar)
    Foo.should_receive(:bar) # fails, expected 1, received 0
    Foo.bar
  end
  it "yes, yes" do
    Foo.should_receive(:bar) # fails, expected 1, received 0
    Foo.should_receive(:bar)
  end
  it "yes, no" do
    Foo.should_receive(:bar) # fails, expected 1, received 0
    Foo.should_not_receive(:bar)
  end
  it "no, yes" do
    Foo.should_not_receive(:bar)
    Foo.should_receive(:bar) # fails, expected 1, received 0
  end
  it "yes(2), doit, yes, doit, doit" do # succeeds
    Foo.should_receive(:bar).exactly(2).times
    Foo.bar
    Foo.should_receive(:bar)
    Foo.bar
    Foo.bar
  end
end

答案 1 :(得分:0)

我认为Project.should_not_receive(:all) fetch_all可能会调用all

您可以在没有Project.should_not_receive(:all)

的情况下尝试