在RSpec中测试默认范围

时间:2013-07-23 17:27:20

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

我正在尝试在模型中测试我的默认范围行。

我的测试如下:

it 'orders by ascending name by default' do
  expect(Coaster.scoped.to_sql).to eq Coaster.order(:name).to_sql
end

我的错误是:

expected: "SELECT \"coasters\".* FROM \"coasters\"  ORDER BY name ASC, name"
got: "SELECT \"coasters\".* FROM \"coasters\"  ORDER BY name ASC"

错误第一行末尾的, name部分是什么意思,我该如何解决?

更新:

我的测试:

  describe 'default scope' do
    let!(:coaster_one) { FactoryGirl.create(:coaster, name: "Tower of Terror") }
    let!(:coaster_two) { FactoryGirl.create(:coaster, name: "Apocalypse") }

    it 'orders by ascending name' do
      Coaster.all.should eq [:coaster_two, :coaster_one]
    end
  end

我的错误:

expected: [:coaster_two, :coaster_one]
            got: [#<Coaster id: 5, name: "Apocalypse", height: "60", speed: 60.0, length: "160", inversions: 4, material: nil, notes: nil, lat: nil, lng: nil, manufacturer_id: nil, park_id: 408, created_at: "2013-07-23 20:48:52", updated_at: "2013-07-23 20:48:52", slug: "apocalypse-at-alton-towers", style: nil, covering: nil, ride_style: nil, model: nil, layout: nil, order: nil, dates_ridden: nil, times_ridden: nil>, #<Coaster id: 4, name: "Tower of Terror", height: "60", speed: 60.0, length: "160", inversions: 4, material: nil, notes: nil, lat: nil, lng: nil, manufacturer_id: nil, park_id: 407, created_at: "2013-07-23 20:48:52", updated_at: "2013-07-23 20:48:52", slug: "tower-of-terror-at-alton-towers", style: nil, covering: nil, ride_style: nil, model: nil, layout: nil, order: nil, dates_ridden: nil, times_ridden: nil>]

       (compared using ==)

更新2:

看起来Rails 4不赞成使用default_scope,所以鉴于此,我已经从我的模型中删除了default_scope并将其替换为标准范围。

新范围现在是:

scope :by_name_asc, lambda { order("name ASC") }

我的相关测试是:

  describe 'scopes' do
    let!(:coaster_one) { FactoryGirl.create(:coaster, name: "Tower of Terror") }
    let!(:coaster_two) { FactoryGirl.create(:coaster, name: "Apocalypse") }

    it "orders coasters by ascending name" do
      Coaster.by_name_asc.should eq [:coaster_two, :coaster_one]
    end
  end

运行此测试时,我得到:

  1) Coaster scopes orders coasters by ascending name
     Failure/Error: Coaster.by_name_asc.should eq [:coaster_two, :coaster_one]

       expected: [:coaster_two, :coaster_one]
            got: [#<Coaster id: 15, name: "Apocalypse", height: "60", speed: 60.0, length: "160", inversions: 4, material: nil, notes: nil, lat: nil, lng: nil, manufacturer_id: nil, park_id: 528, created_at: "2013-07-24 22:36:50", updated_at: "2013-07-24 22:36:50", slug: "apocalypse-at-alton-towers", style: nil, covering: nil, ride_style: nil, model: nil, layout: nil, order: nil, dates_ridden: nil, times_ridden: nil>, #<Coaster id: 14, name: "Tower of Terror", height: "60", speed: 60.0, length: "160", inversions: 4, material: nil, notes: nil, lat: nil, lng: nil, manufacturer_id: nil, park_id: 527, created_at: "2013-07-24 22:36:50", updated_at: "2013-07-24 22:36:50", slug: "tower-of-terror-at-alton-towers", style: nil, covering: nil, ride_style: nil, model: nil, layout: nil, order: nil, dates_ridden: nil, times_ridden: nil>]

       (compared using ==)

       Diff:
       @@ -1,2 +1,2 @@
       -[:coaster_two, :coaster_one]
       +[#<Coaster id: 15, name: "Apocalypse", height: "60", speed: 60.0, length: "160", inversions: 4, material: nil, notes: nil, lat: nil, lng: nil, manufacturer_id: nil, park_id: 528, created_at: "2013-07-24 22:36:50", updated_at: "2013-07-24 22:36:50", slug: "apocalypse-at-alton-towers", style: nil, covering: nil, ride_style: nil, model: nil, layout: nil, order: nil, dates_ridden: nil, times_ridden: nil>, #<Coaster id: 14, name: "Tower of Terror", height: "60", speed: 60.0, length: "160", inversions: 4, material: nil, notes: nil, lat: nil, lng: nil, manufacturer_id: nil, park_id: 527, created_at: "2013-07-24 22:36:50", updated_at: "2013-07-24 22:36:50", slug: "tower-of-terror-at-alton-towers", style: nil, covering: nil, ride_style: nil, model: nil, layout: nil, order: nil, dates_ridden: nil, times_ridden: nil>]

     # ./spec/models/coaster_spec.rb:10:in `block (3 levels) in <top (required)>'

关于出了什么问题的任何想法?

6 个答案:

答案 0 :(得分:17)

测试默认范围的更好方法是使用具有预期输出的实际数据。为您的类创建虚拟对象,然后查询该类并将结果与​​您期望的正确顺序进行比较:

describe 'default scope' do
  let!(:book_one) { Book.create(name: "The Count of Monte Cristo") }
  let!(:book_two) { Book.create(name: "Animal Farm") }

  it 'orders by ascending name' do
    Book.all.should eq [book_two, book_one]
  end
end

let!(:book_one)创建Book的实例,并将其分配给名为book_one的局部变量。它的name属性是基督山的计数let!(:book_two)做了类似的事情。

如果默认顺序为name ASC,则查询Book模型应返回与book_two的ActiveRecord关系作为第一个元素(“Animal ...”)和{{ 1}}作为第二个元素(“The C ...”)。

我们可以按如下方式测试这个期望:

book_one

这不会测试您的SQL,但会测试代码的直接输出,这更有用。它也与数据库无关。

答案 1 :(得分:3)

您的示例是将第二个订单字词添加到默认值,因此您将获得name ASC, name。尝试首先使用reorder('')剥离第一个订单子句,例如:

expect(Coaster.scoped.to_sql).to eq Coaster.reorder('').order('name ASC').to_sql

答案 2 :(得分:2)

干涸,使用共享示例

最有可能的是,您将拥有多个具有类似默认范围的模型(如果没有,大多数情况下忽略此答案),因此您可以将此Rspec示例放入shared_example,您可以从各种模型中调用它规格。

我首选的检查默认范围的方法是确保默认的ActiveRecord::Relation具有预期的子句(orderwhere或者不管情况如何),如下所示: / p>

<强>规格/支持/ shared_examples / default_scope_examples.rb

shared_examples_for 'a default scope ordered by name' do

  it 'adds an ordered by name clause' do
    described_class.scoped.order_clauses.should include("name")
  end

end

然后在您的Coaster规范(以及具有相同默认范围的任何其他规范)中,您只需要:

<强>规格/模型/ coaster_spec.rb

describe Coaster

  it_behaves_like 'a default scope ordered by name'

  # other spec examples #

end

最后,你可以看到这种模式如何扩展到各种默认范围,并保持干净,有条理,最重要的是,DRY。

答案 3 :(得分:0)

关于上次更新,应该是:

Coaster.all.should eq [coaster_two, coaster_one]

而不是:

Coaster.all.should eq [:coaster_two, :coaster_one]

答案 4 :(得分:0)

我们经常尝试使用工厂和实际值来测试范围。当我们只想确认范围设置正确时,我们会执行以下操作:

  describe '.sorted' do
    let(:nodes) { described_class.sorted.order_values }
    let(:columns) { nodes.map { |n| n.instance_variable_get('@expr').name } }

    it do
      expect(columns)
        .to eq [:service_type, :mri_type, :ct_slices, :mri_magnet_strength,
                :created_at]
    end

    it { expect(nodes.map(&:direction)).to eq [:asc, :asc, :asc, :asc, :asc] }
  end

这是对以下内容的测试:

  scope :sorted, (
    lambda do
      order(:service_type, :mri_type, :ct_slices, :mri_magnet_strength,
            :created_at)
    end
  )

答案 5 :(得分:0)

您可能会收到一些关于Mohamad答案的弃用警告,要解决,只需使用expectto

it 'orders by ascending name' do
  expect(Book.all).to eq [book_two, book_one]
end