簡體   English   中英

通過重構 test_helper 使用 Devise 設置 minitest

[英]setting up minitest with Devise with refactoring test_helper

Rails 7 應用程序使用 devise 進行身份驗證。

fixture(模型測試通過所有類):

one: 
  email: 'me@mail.co'
  encrypted_password: <%= User.new.send(:password_digest, '12345678')

test_helper 試圖在實際的 controller 測試中將登錄和操作過程重構為短的一行 ( test_access )

require 'simplecov'
SimpleCov.start 'rails'

ENV["RAILS_ENV"] ||= "test"
require_relative "../config/environment"
require "rails/test_help"
require 'webmock/minitest'
require 'barby/outputter/png_outputter'
require 'barby/barcode/ean_13'

class ActiveSupport::TestCase
  fixtures :all
  include Devise::Test::IntegrationHelpers
  include Warden::Test::Helpers

  parallelize(workers: :number_of_processors)

  def log_in(user)
    if integration_test?
      login_as(user, :scope => :user)
    else
      sign_in(user)
    end
  end

  class Minitest::Test

    def setup
      @one = users(:one)
    end
  end

  private
    def test_access(user, action)
      get '/users/sign_in'
puts user.inspect
      sign_in user
# puts user_signed_in?
# puts current_user.manager?
puts user.manager?
      post user_session_url
      follow_redirect!
puts action
      get action
      assert_response :success
    end

和 controller_test(目前處於中間重構級別,因為該方法應該擴展到一組用戶)

require "test_helper"

class LokationsControllerTest < ActionDispatch::IntegrationTest
  include Devise::Test::IntegrationHelpers


  setup do
    @users_allowed = [ @one ]
    @actions = [dummy_lokations_url]
  end

  test "should get dummy" do
    @actions.each do |action|
      test_access(@one, action)
      assert_response :success
    end
  end

問題是似乎沒有辦法驗證用戶是否登錄,盡管包含 devise 集成助手。 控制台中的output指向正確的值,但結果與應該應用的方法相反

#<User id: 760812109, email: [...]>
true
http://www.example.com/lokations/dummy
[...]
Expected response to be a <2XX: success>, but was a <302: Found> redirect to <http://www.example.com/>
Response body: <html><body>You are being <a href="http://www.example.com/">redirected</a>.</body></html>

因為應用於操作的 before_action 方法指定

  def index_manager
    if user_signed_in? && current_user.manager?
    else
      redirect_to root_path and return
    end
  end

上面哪里錯了? (我一直懷疑可能缺少某些東西,因為過去已成功遵循此模式)

抱歉,但這不是一個好的/可接受的重構。

  • 永遠不要設置密碼摘要。 這應該只被加密密碼的底層系統知道。 您的代碼和測試甚至不應該知道它的存在。 您的代碼應該只使用明文設置密碼屬性。

  • 這些是集成而不是 controller 測試。 Controller 測試是遺留應用程序中使用的ActionController::TestCase的子類。 不應使用它們。 雖然這看起來像是在挑剔,但您只是將自己和他人混為一談而感到困惑。

  • 避免將一堆垃圾修補到ActiveSupport::TestCase或更糟的Minitest::Test中。 如果你必須 monkeypatch 那么將你的方法寫在一個模塊中並包含它以便堆棧跟蹤指向那里。 但是在 class 層次結構中的正確級別進行。 例如,將用於集成和系統測試的幫助程序分開,並將它們包含到ActionDispatch::IntegrationTestActionDispatch::SystemTest中,而不是污染所有測試。

  • 如果您需要添加很多額外的行為,請不要重新打開 class。而是創建您自己的測試 class,它繼承自ActionDispatch::IntegrationTest並讓您的測試子類化它。

  • 如果您有要在多個測試中重用的代碼,例如測試設置,請使用可以包含在實際需要的地方的模塊。

  • test_access不屬於樹上的共享子類。 這屬於實際集成測試,涵蓋您的身份驗證系統流程或特定資源已正確授權。

  • Devise 是一個認證系統(誰是用戶?)——但你在這里做的是授權(誰能做什么?)。 Devise 除了阻止未經身份驗證的用戶訪問外,不提供任何類型的授權。 也不應該(參見SRP )。

  • index_manager方法不是實現授權的好方法。 如果您想重新發明輪子,請確保引發異常並使用rescue_from以便回調鏈停止,這樣您就不會重復自己。 您可能需要查看現有系統,例如 CanCanCan 和 Pundit。

你在這里真正想要的更像是:

class AuthorizationError < StandardError
end
class ApplicationController
  before_action :authenticate_user! # use an opt-out secure by default setup.
  rescue_from AuthorizationError, with: :deny_access

  private

  def authorize_manager!
    unless current_user.manager?
      raise AuthorizationError.new("You need to be a manager to access this resource") 
    end
  end

  def deny_access(exception)
    redirect_to root_path, error: exception.message
  end
end
module AuthorizationIntegrationHelpers
  def assert_denies_access
    follow_redirect!
    assert_current_path root_path
    # ...
  end
end
class FoosFlowTest < ActionDispatch::IntegrationTest
  include AuthorizationIntegrationHelpers
  include Warden::Test::Helpers

  test "should not be accessable to non-managers" do
    login_as(users(:one))
    get '/foos'
    assert_denies_access
  end
end

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM