【问题标题】:Stubbing named_scope in an RSpec Controller在 RSpec 控制器中存根 named_scope
【发布时间】:2023-03-31 02:53:01
【问题描述】:

对于这种情况,我找不到任何东西。我有一个这样定义命名范围的模型:

class Customer < ActiveRecord::Base
  # ...
  named_scope :active_customers, :conditions => { :active => true }
end

我正试图在我的控制器规范中将它存根:

# spec/customers_controller_spec.rb
describe CustomersController do
  before(:each) do
    Customer.stub_chain(:active_customers).and_return(@customers = mock([Customer]))
  end

  it "should retrieve a list of all customers" do
    get :index
    response.should be_success
    Customer.should_receive(:active_customers).and_return(@customers)
  end
end

这不起作用并且失败了,说客户期望 active_customers 但收到了 0 次。在我的索引操作的实际控制器中,我有@customers = Customer.active_customers。我缺少什么让这个工作?可悲的是,我发现编写代码比考虑测试/规范并编写代码更容易,因为我知道规范描述的是什么,而不是如何告诉 RSpec 我想做什么。

【问题讨论】:

    标签: ruby-on-rails rspec


    【解决方案1】:

    我认为stubsmessage expectations 存在一些混淆。消息期望基本上是存根,您可以在其中设置所需的预设响应,但它们也测试被测试代码要进行的调用。相反,存根只是对方法调用的预设响应。但是不要将存根与对同一方法的消息期望混在一起进行测试,否则会发生不好的事情...

    回到你的问题,有两件事(或更多?)需要在这里指定:

    1. 当您对index 执行get 时,CustomersController 会调用Customer#active_customersCustomer#active_customers 在本规范中返回什么并不重要。
    2. active_customers named_scope 实际上确实返回了active 字段为true 的客户。

    我认为您正在尝试执行第 1 项。如果是这样,请删除整个存根并在您的测试中简单地设置消息期望:

    describe CustomersController do
      it "should be successful and call Customer#active_customers" do
        Customer.should_receive(:active_customers)
        get :index
        response.should be_success
      end
    end
    

    在上述规范中,您没有测试它返回的内容。没关系,因为这是规范的意图(尽管您的规范太接近实现而不是行为,但这是一个不同的主题)。如果您希望对active_customers 的调用特别返回某些内容,请继续并将.and_returns(@whatever) 添加到该消息期望中。故事的另一部分是测试active_customers 是否按预期工作(即:对数据库进行实际调用的模型规范)。

    【讨论】:

    • 感谢您的解释(我正在关注 RSpec 教程,这就是使用模拟/存根执行操作的方式)。但是,当我尝试您发布的代码时,我得到了同样的错误: 预期:active_customers(任何参数)一次,但收到0次。尽管在 CustomersController#index 中调用了 Customer.active_customers。
    • 对不起,我的错。对get :index 的调用应该在消息期望之后。我正在纠正答案,以便期望是规范中的第一行。
    • 好的,现在可以了。我习惯了正常的测试过程,其中 get :index 在你测试之前出现,所以这似乎是问题..它与 RSpec 相反。我想我需要更多练习 RSPec。谢谢!
    • 确实感觉颠倒了,我同意。您所追求的被称为“测试间谍”,您可以在其中测试过去时态的期望(Customer.should have_received(:active_customers) 在规范的底部。RSpec 的模拟框架不支持它,但是 RR,not-a -mock 和 jferry 的 mocka 分支确实支持它们并且与 RSpec 兼容。在此处阅读:xunitpatterns.com/Test%20Spy.htmlrobots.thoughtbot.com/post/159805295/spy-vs-spyrspec.info/documentation/mocks/other_frameworks.html 或如何在 RSpec 中使用...
    【解决方案2】:

    如果你想测试你收到的客户记录数组,你应该在 mock 周围放置数组,如下所示:

    Customer.stub_chain(:active_customers).and_return(@customers = [mock(Customer)])
    

    【讨论】:

      【解决方案3】:

      stub_chain 对我来说效果最好。

      我有一个控制器在调用

      ExerciseLog.this_user(current_user).past.all
      

      而且我可以像这样存根

      ExerciseLog.stub_chain(:this_user,:past).and_return(@exercise_logs = [mock(ExerciseLog),mock(ExerciseLog)])
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-07-28
        • 1970-01-01
        • 2011-03-11
        • 1970-01-01
        • 2013-07-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多