繁体   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