[英]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::IntegrationTest
和ActionDispatch::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.