我在Ruby on Rails中有这个User
类:
class User < ActiveRecord::Base
after_destroy :ensure_an_admin_remains
private
def ensure_an_admin_remains
if User.where("admin = ?", true).count.zero?
raise "Can't delete Admin."
end
end
end
这很有效,如果有人意外删除了管理员用户,则会导致数据库回滚。
问题是它似乎打破了用户删除操作,即使在使用非管理员用户(由Factory Girl生成)进行测试时也是如此。这是我的user_controller_spec.rb
:
describe 'DELETE #destroy' do
before :each do
@user = create(:non_admin_user)
sign_in(@user)
end
it "deletes the user" do
expect{
delete :destroy, id: @user
}.to change(User, :count).by(-1)
end
end
每当我运行此测试时,我都会收到此错误:
Failure/Error: expect{
count should have been changed by -1, but was changed by 0
不应该有任何错误,因为@user
的admin属性默认设置为false。
有人可以帮助我吗?
...谢谢
答案 0 :(得分:1)
我可能错了,但是, 你的规范从空数据库开始吧?因此,您的数据库中没有管理员用户。 因此,当您调用delete时,您将始终拥有User.where(“admin =?”,true).count等于零
尝试在测试前创建用户管理员
describe 'DELETE #destroy' do
before :each do
create(:admin_user)
@user = create(:non_admin_user)
sign_in(@user)
end
it "deletes the user" do
expect{
delete :destroy, id: @user
}.to change(User, :count).by(-1)
end
end
答案 1 :(得分:1)
我会做出以下改变:
before_destroy :ensure_an_admin_remains
def ensure_an_admin_remains
if self.admin == true and User.where( :admin => true ).count.zero?
raise "Can't delete Admin."
end
end
答案 2 :(得分:1)
另一种方法是使被调用函数ensure_an_admin_remains
成为公共函数,例如check_admin_remains
。
然后,您可以测试check_admin_remains
的逻辑,就像它是任何其他函数一样。
然后在另一个测试中,您可以确保在destroy上调用函数而不进行任何数据库交互,如下所示:
let(:user) { build_stubbed(:user) }
it 'is called on destroy' do
expect(user).to receive(:check_admin_remains)
user.run_callbacks(:destroy)
end
答案 3 :(得分:0)
你不应该提高控制流量。您可以在回调期间暂停以防止记录被提交。
我已经为其他任何试图解决如何正确解决Rails 5的问题的人提出了一些答案
class User < ActiveRecord::Base
before_destroy :ensure_an_admin_remains
private def ensure_an_admin_remains
return unless admin && User.where(admin: true).limit(2).size == 1
errors.add(:base, "You cannot delete the last admin.")
throw :abort
end
end