版本:
> ruby -v
ruby 3.3.6 (2024-11-05 revision 75015d4c1f) [arm64-darwin24]
> rails -v
Rails 8.0.0.1
user.rb 模型:
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
:omniauthable, omniauth_providers: [:google_oauth2]
enum :status, { active: 'active', blocked: 'blocked' }
belongs_to :role, optional: true
has_many :role_permissions, through: :role
has_many :permissions, through: :role_permissions
validates :email, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP }
validates :encrypted_password, presence: true
validates :full_name, length: { maximum: 255 }, allow_nil: true
validates :uid, length: { maximum: 255 }, allow_nil: true
validates :provider, length: { maximum: 255 }, allow_nil: true
validates :avatar_url, length: { maximum: 255 }, allow_nil: true
validates :status, inclusion: { in: statuses.keys }
def has_permission?(action, resource)
return true if permissions.exists?(name: '*:*')
permissions.exists?(name: "#{action}:#{resource}")
end
def self.from_omniauth(auth)
user = where(provider: auth.provider, uid: auth.uid).first_or_initialize
if user.new_record?
user.email = auth.info.email
user.password = Devise.friendly_token[0, 20]
user.full_name = auth.info.name
user.avatar_url = auth.info.image
user.role = Role.find_by(name: 'default') || nil
elsif user.email != auth.info.email
Rails.logger.warn "Email conflict: Google email #{auth.info.email} does not match existing user email #{user.email}."
end
user.save!
user
end
end
users_controller.rb:
class UsersController < ApplicationController
before_action :set_user, except: [:index]
def index
@users = User.all
end
def edit
end
def sh ow
@user
end
def update
if params[:user][:status].present?
@user.status = params[:user][:status]
end
if params[:user][:role_id].present?
role = Role.find_by(id: params[:user][:role_id])
if role
@user.role = role
else
flash[:alert] = "Invalid role selected."
render :edit and return
end
end
if @user.save
redirect_to users_path, notice: 'User was successfully updated.'
else
render :edit
end
end
def destroy
@user.destroy
flash[:notice] = "User was successfully deleted."
redirect_to users_path
end
private
def user_params
params.require(:user).permit(:full_name, :email, :status, :avatar_url, :role_id)
end
def set_user
@user = User.find_by(id: params[:id])
unless @user
flash[:alert] = "User not found."
redirect_to users_path
end
end
def check_user_permission
action = params[:action]
resource = controller_name
puts "find me here"
puts action
puts resource
unless current_user.has_permission?(action, resource)
redirect_to root_path, alert: "You don't have permission to perform this action on #{resource}."
end
end
end
规范/控制器/users_controller.rb:
require 'rails_helper'
RSpec.describe UsersController, type: :controller do
include Devise::Test::ControllerHelpers
let(:admin) { create(:user, :admin) }
let(:user) { create(:user) }
describe 'GET #index' do
it 'returns a successful response' do
sign_in admin
get :index
expect(response).to be_successful
expect(assigns(:users)).to eq([admin, user])
end
end
end
规范/工厂/权限.rb:
FactoryBot.define do
factory :permission do
name { "" }
trait :admin do
name { "*:*"}
end
end
end
规格/工厂/roles.rb:
FactoryBot.define do
factory :role do
name { "default" }
permissions { [create(:permission)]}
trait :admin do
name { "admin" }
permissions { [create(:permission, :admin)] }
end
end
end
规格/工厂/用户.rb:
FactoryBot.define do
factory :user do
email { Faker::Internet.email }
encrypted_password { Faker::Internet.password }
full_name { Faker::Name.name }
password { Faker::Internet.password }
role { create(:role) }
status { "active" }
provider { 'google' }
uid { Faker::Internet.uuid }
avatar_url { Faker::Avatar.image }
trait :active do
status { "active" }
end
trait :blocked do
status { "blocked" }
end
trait :admin do
role { create(:role, :admin) }
end
end
end
运行测试时出现错误
/bin/zsh -c "bash -c 'env RBENV_VERSION=3.3.6 /opt/homebrew/Cellar/rbenv/1.3.0/libexec/rbenv exec ruby -x /Users/owner/github/work/pms/bin/bundle exec ruby /Users/owner/.rbenv/versions/3.3.6/bin/rspec /Users/owner/github/work/pms/spec/controllers/users_controller_spec.rb --require teamcity/spec/runner/formatter/teamcity/formatter --format Spec::Runner::Formatter::TeamcityFormatter --example UsersController'"
Testing started at 12:46 PM ...
Run options: include {:full_description=>/UsersController/}
Deprecation Warnings:
Rails 7.1 has deprecated the singular fixture_path in favour of an array.You should migrate to plural:
If you need more of the backtrace for any of these deprecations to
identify where to make the necessary changes, you can configure
`config.raise_errors_for_deprecations!`, and it will turn the
deprecation warnings into errors, giving you the full backtrace.
RuntimeError: Could not find a valid mapping for #<User id: 1, email: [FILTERED], full_name: "Steve Weber", uid: "c799b178-a201-483c-9546-32ef58866e2b", provider: "google", avatar_url: "https://robohash.org/laborenemout.png?size=300x300...", created_at: "2024-12-15 07:16:37.643769000 +0000", updated_at: "2024-12-15 07:16:37.643769000 +0000", status: "active", role_id: 1>
0) UsersController GET #index returns a successful response
Failure/Error: sign_in admin
RuntimeError:
Could not find a valid mapping for #<User id: 1, email: [FILTERED], full_name: "Steve Weber", uid: "c799b178-a201-483c-9546-32ef58866e2b", provider: "google", avatar_url: "https://robohash.org/laborenemout.png?size=300x300...", created_at: "2024-12-15 07:16:37.643769000 +0000", updated_at: "2024-12-15 07:16:37.643769000 +0000", status: "active", role_id: 1>
# ./spec/controllers/users_controller_spec.rb:13:in `block (3 levels) in <top (required)>'
1 deprecation warning total
1 example, 1 failure, 0 passed
Finished in 0.153122 seconds
Process finished with exit code 1
规范/rails_helper.rb:
# This file is copied to spec/ when you run 'rails generate rspec:install'
require 'spec_helper'
require 'devise'
ENV['RAILS_ENV'] ||= 'test'
require_relative '../config/environment'
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'
require 'capybara/rspec'
begin
ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
abort e.to_s.strip
end
RSpec.configure do |config|
config.include Devise::Test::ControllerHelpers, type: :controller
config.include Devise::Test::ControllerHelpers, type: :view
config.include Devise::Test::IntegrationHelpers, type: :request
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.use_transactional_fixtures = true
config.infer_spec_type_from_file_location!
config.filter_rails_from_backtrace!
end
Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :rspec
with.library :rails
end
end
有谁知道为什么会这样吗?
我认为问题出在设计或其他事情上,我想知道发生这种情况的原因
不要使用控制器规格。而是编写请求规格:
require 'rails_helper'
RSpec.describe 'User Administration', type: :request do
include Devise::Test::ControllerHelpers
let(:admin) { create(:user, :admin) }
let(:user) { create(:user) }
context "when signed in as an admin" do
before do
login_as admin
end
it 'returns a successful response' do
get :index
expect(response).to be_successful
end
end
end
这将创建一个实际上通过中间件堆栈(包括 Devise)的规范,并且不容易出现错误。不鼓励使用控制器规格。保留它们只是为了兼容旧版本。
assigns
方法早在 2015 年就被废弃了,所以除非你安装 rails-controller-testing
gem(不要),否则它将无法工作。
如果您这样做是因为您正在阅读一本书/教程,您可能想尝试找到反映现代最佳实践的更好来源。
至于实际的错误,这是因为
Devise::Test::ControllerHelpers
用于消除整个该死的身份验证层的模拟无法正常工作。不过我不会再在这上面浪费时间了。