在Spec中测试Rails模型,如何创建有效的示例?

时间:2018-05-18 05:39:48

标签: ruby-on-rails rspec factory-bot

我正在尝试使用Rspec和FactoryBot在Rails中测试我的模型。我正在使用let!

let!(:account1) { create(:account, :year) }
let!(:account2) { create(:account, :three_months) }
let!(:account3) { create(:account, :month) }
let!(:account4) { create(:account, :day) }

我正在测试我的作用域是否返回正确的记录数组。我的假设是,还应该有一些“坏数据”,以测试范围是否不返回它不应该返回的东西。例如:

  describe ".seen_last_two_months" do
    subject { Account.seen_last_two_months.to_a }
    it { is_expected.to eq([account3, account4]) }
  end

看来,当测试量增加时,速度会显着下降。似乎每个调用let!的测试都会命中数据库,从而创建一个新记录。

  1. 我设置测试的方式是否正确,那么我是否还要测试查询不希望返回的记录?
  2. 有没有办法只调用一次创建记录?

2 个答案:

答案 0 :(得分:1)

  
      
  1. 我设置测试的方式是否正确,那么我是否还要测试查询不希望返回的记录?
  2.   

是的,只是你应该。

  
      
  1. 有没有办法只调用一次创建记录?
  2.   

是的,您需要创建这些记录来测试特定范围。因此,只应针对该特定测试调用它们。您可以将它们移动到与范围测试相关的# Keep this one without `!`, so it will be called (in other tests), if and when needed let(:account1) { create(:account, :year) } describe ".seen_last_two_months" do let!(:account1) { create(:account, :year) } let!(:account2) { create(:account, :three_months) } let!(:account3) { create(:account, :month) } let!(:account4) { create(:account, :day) } subject { Account.seen_last_two_months.to_a } it { is_expected.to eq([account3, account4]) } end 块,然后不会为其他测试调用它们。

before(:all)

<强>更新

如果您只想为所有测试创建一次记录,请改用let。看起来在before(:all)块中调用before(:all) do @account1 = create(:account, :year) @account2 = create(:account, :three_months) @account3 = create(:account, :month) @account4 = create(:account, :day) end describe ".seen_last_two_months" do subject { Account.seen_last_two_months.to_a } it { is_expected.to eq([@account3, @account4]) } end 定义的变量是一个坏主意,而是使用实例变量:

account*

在测试中将所有@account*替换为{{1}}。

请记住,对象中任何测试所做的更改也会反映在下一个测试中。

答案 1 :(得分:0)

您可以创建一次所有帐户并测试它们上的所有范围。只需在it块下移动所有内容,如下所示:

context "scopes" do
  it 'selects accounts correctly' do
    let!(:account1) { create(:account, :year) }
    let!(:account2) { create(:account, :three_months) }
    let!(:account3) { create(:account, :month) }
    let!(:account4) { create(:account, :day) }

    expect(Account.seen_last_two_months.to_a).to eq([account3, account4])
    expect(Account.another_scope.to_a).to eq([account1, account2])
    expect(Account.one_more_scope.to_a).to eq([account2])
  end
end

但是这种规范更难维护,您需要在实例中指定许多不同的属性来检查所有范围。我只对类似的范围使用这种方式,例如按状态显示范围(&#39;活动&#39;,&#39;非活动&#39;,&#39;存档&#39;)。

顺便说一句:提供更具描述性的名称总是一个好主意,比如

let!(:year_ago) { create(:account, :year) }