简体   繁体   English

通过重构 test_helper 使用 Devise 设置 minitest

[英]setting up minitest with Devise with refactoring test_helper

Rails 7 application using devise for authentication. Rails 7 应用程序使用 devise 进行身份验证。

fixture (model tests pass for all classes): fixture(模型测试通过所有类):

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

test_helper which attempts to re-factor the login and action process into short one-liners ( test_access ) in the actual controller tests 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

and the controller_test (presently on an intermediate refactoring level, as the method should extend to an array of users)和 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

the problem is there seems to be no way of verifying whether the user is signed in or not, notwithstanding the inclusion of devise integration helpers.问题是似乎没有办法验证用户是否登录,尽管包含 devise 集成助手。 The output in the console points to proper values, but the result is the opposite of the method that should be applied控制台中的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>

because the before_action method applied on the action specifies因为应用于操作的 before_action 方法指定

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

Where is the above mistaken?上面哪里错了? (I have a nagging doubt that something may be missing as this pattern has been successfully followed in the past) (我一直怀疑可能缺少某些东西,因为过去已成功遵循此模式)

Sorry but this is just not a good/acceptable refactor.抱歉,但这不是一个好的/可接受的重构。

  • Do not set the password digest - ever.永远不要设置密码摘要。 That should only be known by the underlying system that encrypts the password.这应该只被加密密码的底层系统知道。 Your code and tests should not even know that it exists.您的代码和测试甚至不应该知道它的存在。 Your code should only ever set the password attribute with a cleartext.您的代码应该只使用明文设置密码属性。

  • These are integration and not controller tests.这些是集成而不是 controller 测试。 Controller tests are subclasses of ActionController::TestCase used in legacy applications. Controller 测试是遗留应用程序中使用的ActionController::TestCase的子类。 They should not be used.不应使用它们。 While this might seem like nickpicking you're only confusing yourself and others by conflating them.虽然这看起来像是在挑剔,但您只是将自己和他人混为一谈而感到困惑。

  • Avoid monkeypatching a bunch of junk into ActiveSupport::TestCase or even worse Minitest::Test .避免将一堆垃圾修补到ActiveSupport::TestCase或更糟的Minitest::Test中。 If you MUST monkeypatch then write your methods in a module and include it so that the stack trace points there. 如果你必须 monkeypatch 那么将你的方法写在一个模块中并包含它以便堆栈跟踪指向那里。 But do it at the correct level in the class heirarchy.但是在 class 层次结构中的正确级别进行。 For example separate between the helpers for integration and system tests and include them into ActionDispatch::IntegrationTest and ActionDispatch::SystemTest instead of polluting all your tests.例如,将用于集成和系统测试的帮助程序分开,并将它们包含到ActionDispatch::IntegrationTestActionDispatch::SystemTest中,而不是污染所有测试。

  • If you need to add a lot of additional behavior don't reopen the class. Instead create your own test class which inherits from ActionDispatch::IntegrationTest and have your tests subclass it.如果您需要添加很多额外的行为,请不要重新打开 class。而是创建您自己的测试 class,它继承自ActionDispatch::IntegrationTest并让您的测试子类化它。

  • If you have code that you want to reuse in multiple tests like the test setup use modules that can be included where they are actually needed.如果您有要在多个测试中重用的代码,例如测试设置,请使用可以包含在实际需要的地方的模块。

  • test_access doesn't belong in the shared subclass way up the tree. test_access不属于树上的共享子类。 This belongs in an actual integration test which covers your authentication system flows or that a specific resource is authorized correctly.这属于实际集成测试,涵盖您的身份验证系统流程或特定资源已正确授权。

  • Devise is an authentication system (who is the user?) - but what you're doing here is authorization (who can do what?). Devise 是一个认证系统(谁是用户?)——但你在这里做的是授权(谁能做什么?)。 Devise doesn't provide any kind of authorization besides preventing access for users that are not authenticated. Devise 除了阻止未经身份验证的用户访问外,不提供任何类型的授权。 Neither should it (see SRP ).也不应该(参见SRP )。

  • The index_manager method isn't a good way to implement authorization. index_manager方法不是实现授权的好方法。 If you want to reinvent the wheel make sure you raise an exception and use rescue_from so that the callback chain is halted and so that you're not repeating yourself.如果您想重新发明轮子,请确保引发异常并使用rescue_from以便回调链停止,这样您就不会重复自己。 You may want to look into existing systems like CanCanCan and Pundit.您可能需要查看现有系统,例如 CanCanCan 和 Pundit。

What you actually want here is something more like:你在这里真正想要的更像是:

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