Rspec希望在不应该的时候通过

时间:2016-03-05 02:02:02

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

当我运行以下测试时

RSpec.describe LessonsController, type: :controller do
 describe 'GET / index' do
    let(:lesson1) {FactoryGirl.create(:lesson)}
    let(:lesson2) {FactoryGirl.create(:lesson)}

    it 'returns an http success' do
      get :index
      expect(response).to be_success

    end

    it 'returns all the lessons' do
      get :index
      expect(assigns[:lessons]).to eq([])
      expect(assigns[:lessons]).to eq([lesson1, lesson2])

    end
  end
end

第二个预期expect(assigns[:lessons]).to eq([lesson1, lesson2])expected: [#<Lesson id:...>, #<Lesson id:...>] got: #<ActiveRecord::Relation []>失败。

但是,当我运行以下测试时,它全部通过

RSpec.describe LessonsController, type: :controller do
 describe 'GET / index' do
    let(:lesson1) {FactoryGirl.create(:lesson)}
    let(:lesson2) {FactoryGirl.create(:lesson)}

    it 'returns an http success' do
      get :index
      expect(response).to be_success

    end

    it 'returns all the lessons' do
      get :index
      expect(assigns[:lessons]).to eq([lesson1, lesson2])

    end
  end
end

我想知道为什么第二次测试没有失败?我期待第二个规格也失败的原因与第一个相同。

我相信这可能是由于发表声明。

话虽如此,我正在运行rspec-rails,factory_girl_rails和Rails 4.我不相信它是由于污染,因为即使我单独运行测试(焦点),这种效果仍然会发生。

1 个答案:

答案 0 :(得分:1)

首先,我猜你的控制器有这样的代码:

@lessons = Lesson.all 

请记住,它会返回一个ActiveRecord::Relation,它可能在它需要的最后一刻才真正命中数据库。此外,一旦ActiveRecord::Relation获取其结果,将不会重新获取它们,除非您致电.reload

其次,请记住let的工作原理。在您尝试访问该变量之前,不会评估let的代码。所以,你会遇到这样的情况:

describe "Something" do 
  let(:lesson) { Lesson.create! }

  it "makes a lesson" do 
    # right now there are 0 lessons 
    lesson
    # calling `lesson` caused the lesson to be created, 
    # now there is 1 lesson
  end 
end 

第三,当您将ActiveRecord::Relation转换为数组时,它会执行真实的数据库查询(在本例中为select * from lessons)。

考虑到这些因素,我们可以对比两个测试用例。

在第一种情况下,在实际创建课程之前,从数据库中提取课程:

it 'returns all the lessons' do
  get :index
  # No lessons have been created yet 
  # `select * from lessons` returns no results 
  expect(assigns[:lessons]).to eq([])

  # `lessons` is cached. It won't query the database again 
  # calling `lesson1` and `lesson2` creates two lessons, but it's too late 
  # the result has already been cached as []
  expect(assigns[:lessons]).to eq([lesson1, lesson2])
end

在第二种情况下,课程是先创建 ,然后执行数据库查询:

  get :index
  # calling `lesson1` and `lesson2` creates two lessons
  # then the AR::Relation runs a query and finds the two lessons
  expect(assigns[:lessons]).to eq([lesson1, lesson2])

为了证明这一点,这是一个应该通过的例子:

get :index 
expect(assigns[:lessons]).to eq([])
# this causes the lessons to be created 
lessons = [lesson1, lesson2]
# use `.reload` to force a new query:
expect(assigns[:lessons].reload).to eq(lessons)

此外,您可以使用RSpec的let!运行示例之前创建课程