User destroy tests

刪除使用者是一個危險的動作,所以最好寫個測試來確保刪除行為如預期。首先,先在 fixture 裡面設定一個使用者為管理員:

test/fixtures/users.yml

michael:
  name: Michael Example
  email: michael@example.com
  password_digest: <%= User.digest('password') %>
  admin: true

處理有關 action 的測試,通常都會寫在 controller test 裡面,也就是 Users controller test,然後使用 delete 方法向 destroy action 處理 DELETE 請求。我們要確認兩件事:

  1. 如果使用者未登入,會導向登入頁面
  2. 第二,如果登入的使用者不是管理員,會導向到首頁

程式碼如下:

test/controllers/users_controller_test.rb

require 'test_helper'

class UsersControllerTest < ActionController::TestCase

  def setup
    @user       = users(:michael)
    @other_user = users(:archer)
  end
  .
  .
  .
  test "should redirect destroy when not logged in" do
    assert_no_difference 'User.count' do
      delete :destroy, id: @user
    end
    assert_redirected_to login_url
  end

  test "should redirect destroy when logged in as a non-admin" do
    log_in_as(@other_user)
    assert_no_difference 'User.count' do
      delete :destroy, id: @user
    end
    assert_redirected_to root_url
  end
end

注意,上述程式碼使用了 assert_no_difference 確認使用者的數量沒有變化。

上面的測試是確認已登入但沒有權限的使用者不能刪除使用者,不過我們也想檢查管理員可以成功的使用「delete」連結刪除使用者。因為「delete」連結是在使用者列表顯示,所以我們要在 users_index_test.rb 裡面增加這個測試。這個測試的技巧在於,當管理員點擊「delete」連結後,檢查使用者的數量:

assert_difference 'User.count', -1 do
  delete user_path(@other_user)
end

也就是確認向相對應的位址發送 DELETE 請求之後,透過 User.count,加上參數 -1 ,藉此比較刪除前後的使用者數量的變化。

程式碼如下,包含了對管理員以及沒有管理權限的使用者的測試:

test/integration/users_index_test.rb

require 'test_helper'

class UsersIndexTest < ActionDispatch::IntegrationTest

  def setup
    @admin     = users(:michael)
    @non_admin = users(:archer)
  end

  test "index as admin including pagination and delete links" do
    log_in_as(@admin)
    get users_path
    assert_template 'users/index'
    assert_select 'div.pagination'
    first_page_of_users = User.paginate(page: 1)
    first_page_of_users.each do |user|
      assert_select 'a[href=?]', user_path(user), text: user.name
      unless user == @admin
        assert_select 'a[href=?]', user_path(user), text: 'delete'
      end
    end
    assert_difference 'User.count', -1 do
      delete user_path(@non_admin)
    end
  end

  test "index as non-admin" do
    log_in_as(@non_admin)
    get users_path
    assert_select 'a', text: 'delete', count: 0
  end
end

上述程式碼也檢查每個使用者都會有「delete」連結,然後如果是管理員就不做這項檢查(因為管理員本身不會顯示「delete」)。

最後執行測試,應該會成功(Green):

$ bundle exec rake test