2013-03-03 52 views
0

我Ruby on Rails中這個User類:如何在Rails和RSpec中測試after_destroy回調?

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的管理屬性設置爲false默認。

有人可以幫我嗎?

謝謝...

+1

應該是驗證 – apneadiving 2013-03-03 15:04:54

回答

1

我可能是錯的,但是, 你的規格開始與空數據庫吧?所以你的數據庫中沒有管理員用戶。 所以,當你調用刪除,你會永遠擁有User.where(「管理=?」,真).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 
+0

好極了,這就是它!非常感謝... – Tintin81 2013-03-03 17:03:30

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 
+0

這工作同樣好,甚至不需要我來改變我的規格文件。謝謝! – Tintin81 2013-03-03 17:06:13

0

另一種方法是使被調用函數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 
0

你不應該提高對控制流。您可以在回調期間暫停以防止記錄被提交。

我已經改善一個一些問題的答案在這裏的任何人都試圖找出如何正確地做到這一點像Rails 5

class User < ActiveRecord::Base 
    after_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