在 Rails 中运行测试时出现运行时错误

问题描述 投票:0回答:1

版本:

> 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:46PM ...
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

有谁知道为什么会这样吗?

我认为问题出在设计或其他事情上,我想知道发生这种情况的原因

ruby-on-rails devise
1个回答
0
投票

不要使用控制器规格。而是编写请求规格:

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
用于消除整个该死的身份验证层的模拟无法正常工作。不过我不会再在这上面浪费时间了。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.