Rspec 期待在不该通过的时候通过
Rspec expect passing when it should not
当我运行以下测试
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
我想知道为什么第二次测试没有失败?我原以为第二个规范也会因与第一个规范相同的原因而失败。
我相信这可能是由于 let 语句。
话虽如此,我 运行宁 rspec-rails、factory_girl_rails 和 Rails 4. 我不认为这是到期的污染,因为即使我 运行 隔离测试(焦点),这种影响仍然会发生。
首先,我猜你的控制器有这样的代码:
@lessons = Lesson.all
请记住,returns 和 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!
创建课程 before 运行 示例。
当我运行以下测试
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
我想知道为什么第二次测试没有失败?我原以为第二个规范也会因与第一个规范相同的原因而失败。
我相信这可能是由于 let 语句。
话虽如此,我 运行宁 rspec-rails、factory_girl_rails 和 Rails 4. 我不认为这是到期的污染,因为即使我 运行 隔离测试(焦点),这种影响仍然会发生。
首先,我猜你的控制器有这样的代码:
@lessons = Lesson.all
请记住,returns 和 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!
创建课程 before 运行 示例。