【问题标题】:"Could not find a valid mapping for #<User ...>" only on second and successive tests“找不到#<User ...> 的有效映射”仅在第二次和后续测试中
【发布时间】:2011-06-15 19:52:26
【问题描述】:

我正在尝试编写一个请求测试,该测试断言正确的链接出现在应用程序布局上,具体取决于用户是否登录。 FWIW,我正在使用 Devise 进行身份验证。

这是我的规格:

require 'spec_helper'
require 'devise/test_helpers'

describe "Layout Links" do
  context "the home page" do
    context "session controls" do
      context "for an authenticated user" do
        before do
          # I know these should all operate in isolation, but I
          # want to make sure the user is explicitly logged out
          visit destroy_user_session_path

          @user = Factory(:user, :password => "Asd123", :password_confirmation => "Asd123")
          @user.confirm!

          # I tried adding this per the Devise wiki, but no change
          @request.env["devise.mapping"] = Devise.mappings[:user]

          # Now log a new user in
          visit new_user_session_path
          fill_in "Email",    :with => @user.email
          fill_in "Password", :with => "Asd123"
          click_button "Sign in"
          get '/'
        end

        it "should not have a link to the sign in page" do
          response.should_not have_selector(
            '#session a',
            :href => new_user_session_path
          )
        end

        it "should not have a link to registration page" do
          response.should_not have_selector(
            '#session a',
            :href => new_user_registration_path
          )
        end

        it "should have a link to the edit profile page" do
          response.should have_selector(
            '#session a',
            :content => "My Profile",
            :href => edit_user_registration_path
          )
        end

        it "should have a link to sign out page" do
          response.should have_selector(
            '#session a',
            :content => "Logout",
            :href => destroy_user_session_path
          )
        end
      end # context "for an authenticated user"
    end # context "session controls"
  end
end

第一个测试通过,但最后三个都失败并出现错误

Failure/Error: @user = Factory(:user, :password => "Asd123", :password_confirmation => "Asd123")
  RuntimeError:
    Could not find a valid mapping for #<User id: xxx, ...>

我已经在 Devise wiki、Google 群组和搜索结果中搜索了一个原因,但我发现的只是未回答的问题或设置 config.include Devise::TestHelpers, :type =&gt; :controller 的建议,但这仅适用于控制器测试,不适用于请求测试。


更新

我进行了更多故障排除,但无法确定最终引发问题的原因。看看下面的代码。

首先,对于某些上下文,这里是用户工厂声明。它在单元测试中运行良好。

# spec/factories.rb
Factory.define :user do |f|
  f.email { Faker::Internet.email }
  f.email_confirmation { |f| f.email }
  f.password "AbcD3fG"
  f.password_confirmation "AbcD3fG"
  f.remember_me { (Random.new.rand(0..1) == 1) ? true : false }
end

现在,考虑以下集成测试

# spec/requests/user_links_spec.rb
require "spec_helper"

describe "User Links" do
  before(:each) do
    # This doesn't trigger the problem
    # @user = nil

    # This doesn't trigger the problem
    # @user = User.new

    # This doesn't trigger the problem
    # @user = User.create(
    #   :email => "foo@bar.co", 
    #   :email_confirmation => "foo@bar.co", 
    #   :password => "asdf1234", 
    #   :password_confirmation => "asdf1234"
    # )

    # This doesn't trigger the problem
    # @user = User.new
    # @user.email = Faker::Internet.email
    # @user.email_confirmation = @user.email
    # @user.password = "AbcD3fG"
    # @user.password_confirmation = "AbcD3fG"
    # @user.remember_me = (Random.new.rand(0..1) == 1) ? true : false
    # @user.save!

    # This triggers the problem!
    @user = Factory(:user)

    # This doesn't trigger the same problem, but it raises a ActiveRecord::AssociationTypeMismatch error instead. Still no idea why. It was working fine before in other request tests.
    # @user = Factory(:brand)
  end

  context "when using `@user = Factory(:user)` in the setup: " do
    2.times do |i|
      it "this should pass on the 1st iteration, but not the 2nd (iteration ##{i+1})" do
        # This doesn't trigger an error
        true.should_not eql(false)
      end

      it "this should pass on the 1st iteration, but trigger the error that causes all successive test cases to fail (iteration ##{i+1})" do
        # Every test case after this will be borken!
        get '/'
      end

      it "this will fail on all iterations (iteration ##{i+1})" do
        # This will now trigger an error
        true.should_not eql(false)
      end
    end
  end
end

如果我们注释掉或将get '/' 位替换为其他任何内容(或什么都没有),测试都运行良好。

所以,我不知道这是 factory_girl 问题(我倾向于怀疑它,因为我可以在其他地方使用用户工厂而没有问题)还是 Devise 问题(我在设置该 gem 后开始收到这些错误我的应用程序,但我也只有另一个请求测试,它运行良好,但现在出现 AssociationTypeMismatch 错误;相关性≠因果关系...)或 RSpec 问题或其他一些奇怪的边缘案例 gem 冲突。

【问题讨论】:

  • 我发现即使用户实际上并未在任何测试用例中使用,也会发生这种情况。我正在发布票的更新,因为它太大而无法在评论表单中发布。
  • 我在 Devise 邮件组中发现了一个讨论同样问题的线程,但到目前为止也没有答案。 groups.google.com/group/plataformatec-devise/browse_thread/…

标签: devise integration-testing rspec2 factory-bot


【解决方案1】:

将此行添加到您的 routes.rb

# Devise routes
devise_for :users # Or the name of your custom model

【讨论】:

    【解决方案2】:

    感谢:http://blog.thefrontiergroup.com.au/2011/03/reloading-factory-girl-factories-in-the-rails-3-console/

    “Devise 使用类和路由之间的映射,因此当一个工厂构建的对象在控制台重新加载或类重新定义后通过 Devise 时,它​​将失败。”

    将其放入初始化程序或 application.rb

    ActionDispatch::Callbacks.after do
      # Reload the factories
      return unless (Rails.env.development? || Rails.env.test?)
    
      unless FactoryGirl.factories.blank? # first init will load factories, this should only run on subsequent reloads
        FactoryGirl.factories.clear
        FactoryGirl.find_definitions
      end
    end
    

    【讨论】:

    【解决方案3】:

    对于未来的读者:我收到了同样的错误,但原因不同。

    我们的应用有多个用户模型,其中一个衍生自另一个

    为了论证:

    class SimpleUser
    
    class User < SimpleUser
    

    在我的控制器中,我使用了对 SimpleUser(父类)的引用,但 Devise 被配置为使用 User(子类)。

    在调用 Devise::Mapping.find_scope 期间! .is_a 有吗?与对象引用和配置的类进行比较。

    由于我的参考是 SimpleUser,而配置的类是 User,.is_a?失败,因为比较是询问 Parent 类是否是_a?子类,总是假的。

    希望对其他人有所帮助。

    【讨论】:

    • 那么,您对此问题的解决方法是什么?
    • 如果您和我一样看到错误,请确保您使用的类与 Devise 配置使用的类相同(或者子类也可以工作)。就我而言,错误是我试图使用 Devise 配置为使用的类的父类。
    【解决方案4】:

    我的路由文件中出现此错误,因为我有一个 API 端点,我希望允许用户从中重置密码,但我希望用户实际在未命名空间的 Web 视图上更改密码在/api

    这就是我修复路线以使其工作的方式:

    CoolApp::Application.routes.draw do
      namespace :api, defaults: { format: :json } do
        devise_scope :users do
          post 'users/passwords', to: 'passwords#create'
        end
    
        resources :users, only: [:create, :update]
      end
    
      devise_for :users
    
      root to: 'high_voltage/pages#show', id: 'home'
    end
    

    【讨论】:

      【解决方案5】:

      Halfnelson 的解决方案也对我有用,但由于我使用的是 Fabrication gem 而不是 FactoryGirl,因此我需要进行一些调整。这是我放入初始化程序的代码:

      if Rails.env.development? || Rails.env.test?
        ActionDispatch::Callbacks.after do
          Fabrication.clear_definitions
          Rails.logger.debug 'Reloading fabricators'
        end
      end
      

      【讨论】:

        【解决方案6】:

        我知道这是一个旧线程,但我想回答其他遇到此问题的人。不知道我在哪里读到的,但对在另一个论坛上发布此内容的人表示支持。

        总而言之,设计测试助手不能很好地与集成测试配合使用。

        因此,请从规范本身中删除所有对 Devise::TestHelpers 的引用(有些人使用 include,其他人使用 require 'devise/testhelpers')。

        然后在 spec_helper 文件中添加:

        RSpec.configure do |config|
          config.include Devise::TestHelpers, :type => :controller
        end
        

        【讨论】:

        • 正如我在上面的 OP 中指出的那样,这仅适用于控制器测试,而不是我尝试的请求测试。您是说根本不能在集成测试中使用 Devise,只能从控制器测试中使用?
        【解决方案7】:

        以防万一其他人遇到这种情况,出于与我相同的原因-在更改某些配置文件后,我基本上在所有测试中都遇到了相同的问题-原来这是由于 RAILS_ENV 被设置为development 当我意外运行测试时。在添加特定于测试的 rails 初始化程序之前可能值得检查:-)

        【讨论】:

          【解决方案8】:

          在我的情况下,这是 Devis 的 confirm! 方法的问题。所以不要这样做(Minitest::Test 代码):

          setup do
            @admin = create(:admin).confirm!
          end
          

          我已经这样做了:

          setup do
            @admin = create(:admin)
            @admin.confirm!
          end
          

          它成功了:)

          【讨论】:

            【解决方案9】:

            我之前在我的一个应用上以测试用户身份登录并进行了一些测试上传和测试帖子时,曾发生过这种情况。然后我删除了那个测试用户,当我尝试用同一个测试用户再次注册时,我看到了消息“找不到 nil 的有效映射”。我为解决这个问题所做的就是删除我作为该测试用户所做的所有测试上传和测试帖子。然后我再次尝试注册,它成功了。通过这种方式,删除内容更快更简单的方法是使用db browser for sqlite

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2019-07-11
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2010-10-25
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多