RSpec模拟ActiveRecord的问题先查找Where

时间:2011-08-11 13:47:16

标签: ruby-on-rails ruby ruby-on-rails-3 unit-testing rspec

我正在尝试为我的控制器解决我的RSpec测试,但它不起作用,我需要一些帮助来搞清楚。

我的Rspec是:

  before(:each) do
    @topic = mock_model(Topic, :update_attributes => true)
    Topic.stub!(:where).with({:slug=>"some-slug"}).and_return(@topic)
    with_valid_user
  end

  it "should find topic and return object" do
    Topic.should_receive(:where).with("some-slug").and_return(@topic)
    put :update, :topic_slug => "some-slug", :topic => {}
  end

我想测试的控制器逻辑是:

  def get_topic
    @topic = Topic.where(:slug => params[:topic_slug]).first
    @topic
  end

但我得到的输出是:

 Failure/Error: Topic.stub!(:where).with({:slug=>"some-slug"}).first.and_return(@topic)
 NoMethodError:
   undefined method `first' for #<RSpec::Mocks::MessageExpectation:0x104c99910>
 # ./spec/controllers/topics_controller_spec.rb:41

显然,这个方法似乎要求“第一”。我理解在这种特殊情况下有点多余,所以我可以删除它,但不是在我知识的一个漏洞周围,我想学习如何正确地做到这一点(对于这种情况)。

任何人都可以帮助我填补空缺吗?

更新

我按照答案中的建议添加了[@topic]数组,但现在我收到了错误:

  Failure/Error: put :update, :topic_slug => "some-slug", :topic => {}
       Mock "Topic_1001" received unexpected message :slug with (no args)

关于此代码:

  def get_topic
    @topic = Topic.where(:slug => params[:topic_slug]).first
    @topic
  end

参数不能改变(至少不能改变)。非常感谢进一步的帮助!

2 个答案:

答案 0 :(得分:3)

更改此行

Topic.should_receive(:where).with("some-slug").and_return(@topic)

到这个

Topic.should_receive(:where).with("some-slug").and_return([@topic])

你期望数组但返回一个元素。

答案 1 :(得分:2)

stub_chain是代码气味,应该作为最后的手段。它将您的规范与实现细节紧密联系在一起,实现细节可能会因重构而发生变化。

我建议这样的事情:

Topic.should_receive(:with_slug).and_return(@topic)

然后将with_slug方法添加到Topic,您就可以了。