1

User在 Ruby on Rails 中有这个类:

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 。

有人可以帮我吗?

谢谢...

4

4 回答 4

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
于 2013-03-03T16:08:27.537 回答
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
于 2013-03-03T17:02:38.123 回答
1

另一种方法是使被调用函数ensure_an_admin_remains成为公共函数,例如check_admin_remains.

然后,您可以测试它的逻辑,check_admin_remains就好像它是任何其他功能一样。

然后在另一个测试中,您可以确保在没有任何数据库交互的情况下在销毁时调用该函数,如下所示:

let(:user) { build_stubbed(:user) }

it 'is called on destroy' do
  expect(user).to receive(:check_admin_remains)

  user.run_callbacks(:destroy)
end
于 2016-11-17T14:58:54.693 回答
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
于 2017-03-31T03:27:39.953 回答