当我运行以下测试时
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.我不相信它是由于污染,因为即使我单独运行测试(焦点),这种效果仍然会发生。
答案 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!
在运行示例之前创建课程。