TLDR:App.count需要重新加载才能看到创建的记录。为什么?
我发现很多引用测试DELETE方法,如下所示:
expect { delete_request }.to change(App, :count).by(-1)
这是有道理的,适用于某些类似的场景。但是,在测试不应该工作的删除时,我发现了一个问题,例如没有用户登录时。
这是我开始的地方,有两种方法可以测试同样的事情:
require 'rails_helper'
RSpec.describe V1::AppsController, type: :controller do
let(:user) { create(:user) }
let(:app) { create(:app, account: user.account) }
describe 'DELETE destroy when you are not logged in' do
let(:delete_request) { delete :destroy, id: app, format: :json }
it 'does not delete the app (.count)' do
expect { delete_request }.not_to change(App, :count)
end
it 'does not delete the app (.exists?)' do
delete_request
expect(App.exists?(app.id)).to eq(true)
end
end
end
这就是rspec所说的:
V1::AppsController
DELETE destroy when you are not logged in
does not delete the app (.count) (FAILED - 1)
does not delete the app (.exists?)
Failures:
1) V1::AppsController DELETE destroy when you are not logged in does not delete the app (.count)
Failure/Error: expect { delete_request }.not_to change(App, :count)
expected #count not to have changed, but did change from 0 to 1
# ./spec/controllers/v1/delete_test_1.rb:11:in `block (3 levels) in <top (required)>'
2 examples, 1 failure
请注意最令人困惑的部分:expected #count not to have changed, but did change from 0 to 1
。咦?我试图进行非法删除,我的记录数增长一个?另请注意,明确检查主题记录的检查仍然有效。
所以我玩了一些,发现我可以在expect()之前重新加载来修复问题:
it 'does not delete the app (.count)' do
puts "App.count is #{App.count} (after create(:app))"
app.reload
puts "App.count is #{App.count} (after reload)"
expect { delete_request }.not_to change(App, :count)
puts "App.count is #{App.count} (after request)"
end
现在rspec很高兴:
V1::AppsController
DELETE destroy when you are not logged in
App.count is 0 (after create(:app))
App.count is 1 (after reload)
App.count is 1 (after request)
does not delete the app (.count)
does not delete the app (.exists?)
2 examples, 0 failures
从这一切开始,我决定坚持使用exists?
方法。但另一个(可能更大)的问题是,我在互联网上发现的所有测试样本,如果他们看到与我相同的结果,并且假设记录,那么测试创建像expect { create_request }.to change(App, :count).by(1)
这样的记录的所有测试样本都可能是误报。实际上它是一个缓存工件吗?
那么,知道为什么App.count需要reload
才能看到当前值?
答案 0 :(得分:7)
这是因为当ruby调用delete_request
方法时,它会调用app
方法,并创建一个App
要测试此功能,请在调用expect
rspec方法之前创建应用程序:
it 'does not delete the app (.count)' do
app #creates the app
expect { delete_request }.not_to change(App, :count)
end
查看let
的文档:
使用let来定义memoized帮助器方法。该值将被缓存 在同一个示例中跨多个调用但不跨越示例。
请注意,let是懒惰评估的:直到第一个才会评估它 调用它定义的方法的时间。你可以用let!迫使 每个例子之前的方法调用。
https://www.relishapp.com/rspec/rspec-core/v/2-11/docs/helper-methods/let-and-let