【问题标题】:Rails 3, RSpec 2.5: Using should_receive or stub_chain with named scopesRails 3,RSpec 2.5:使用带有命名范围的 should_receive 或 stub_chain
【发布时间】:2025-12-22 09:40:16
【问题描述】:

我使用 Rails 3.0.4 和 RSpec 2.5。在我的控制器中,我大量使用命名范围,例如

@collection = GuestbookEntry.nonreplies.bydate.inclusive.paginate( :page => params[:page], :conditions => { ... })

在我的测试中,我希望能够模拟这种查询的结果,而不是措辞。我认为这样做没有意义

GuestbookEntry.stub_chain(:nonreplies, :bydate, ...).and_return(...)

因为当我决定重新排序命名范围时,此测试将失败。

使用 Rails 2.3 和 RSpec 1.x,效果很好:我可以编写

GuestbookEntry.should_receive(:find).with(:all, :conditions => { ... })

上面的调用将被捕获并正确处理。然而,在 Rails 3 中,由于某种原因,这不再起作用。

为什么?如何在嵌套范围的 result 上设置期望值或存根?由于 Rails 3 的 ActiveModel 中的所有内容都是一个命名范围(感谢 ARel),所以这必须以某种方式实现,否则测试确实会非常脆弱。

谢谢!

更新:另见issue report on GitHub

【问题讨论】:

    标签: ruby-on-rails-3 bdd rspec2 arel activemodel


    【解决方案1】:

    我会将如此复杂的查询包装在它自己的范围内,然后存根。

    【讨论】:

    • 那么我该如何编写规范来验证单个作用域是否完成了它应该做的事情?我的想法是编写多个 simple 作用域,这些作用域 明显 以便它们内部几乎没有任何东西可以“出错”,然后将它们组合起来,因为很难指定作用域(无需借助真实数据填充和查询真实数据库)。
    • 我认为您必须指定每个“完整范围”,否则您将不知道您确实将它们正确组合。即使您指定了正确的组合,您也需要一个整体规范来表明该组合本身是解决问题的正确方法。但是如果你把复杂的作用域放在它自己的作用域中,你至少不必担心“措辞”的顺序,因为它只在一次地方完成。我认为您无法摆脱填充数据库。就个人而言,我发现使用工厂设置真实数据比模拟模型方法更容易(并且“更安全”)。
    【解决方案2】:

    这个问题也困扰了我一段时间!

    我相信行为与 Rails 2 不同的原因是因为在控制器中的变量分配期间不再执行查询。相反,它会根据需要进行延迟加载。

    我同意 Mark Wilden 的观点,最好将所有这些范围封装在一个更大的范围中,并在您的模型中指定该范围。这个作用域显然有一个特定的功能,所以就像指定一个调用其他几个方法的方法的行为一样,你可以指定一个连接几个其他作用域的作用域的行为。

    【讨论】: