“无法在第二次和后续测试中找到#<user ... =”“>”的有效映射</user>

时间:2011-06-15 19:52:26

标签: devise integration-testing rspec2 factory-bot

我正在尝试编写一个请求测试,断言应用程序布局上是否显示正确的链接,具体取决于用户是登录还是注销。 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 => :controller,但这仅适用于控制器测试,而不是请求测试。< / p>


更新

我已经做了一些更多的故障排除,我无法做出最终触发问题的正面或反面。看看下面的代码。

首先,对于某些上下文,这里是用户工厂声明。它在单元测试中工作正常。

# 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问题(我倾向于怀疑它,因为我可以在其他地方使用用户工厂没有问题)或者设计问题(我在设置了这个宝石后开始出现这些错误)我的应用程序,但我也只有一个其他的请求测试,它确实工作正常,但现在得到了AssociationTypeMismatch错误;相关≠因果关系...)或RSpec问题或一些其他奇怪的边缘案例宝石冲突。

9 个答案:

答案 0 :(得分:20)

将此行添加到routes.rb

# Devise 
devise_for :users # Or the name of the model you are using

答案 1 :(得分:9)

感谢: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

答案 2 :(得分:9)

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

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

为了论证:

class SimpleUser

class User < SimpleUser

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

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

由于我的引用是SimpleUser,配置的类是User,所以.is_a?失败,因为比较是询问父类是否__?子类,总是错误的。

希望能帮助别人。

答案 3 :(得分:3)

我的路由文件中出现此错误,因为我有一个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

答案 4 :(得分:1)

我知道这是一个老线程,但我想要回答其他人遇到的问题。不知道我在哪里读到这个,但是在另一个论坛上发布这个帖子的人会道具。

长而短暂的是,设计测试助手在集成测试中效果不佳。

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

然后在spec_helper文件中添加:

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

答案 5 :(得分:1)

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 :(得分:1)

以防其他人遇到这个,出于与我相同的原因 - 在更改某些配置文件后基本上所有测试都遇到了同样的问题 - 事实证明这是由于RAILS_ENV我偶然运行测试时设置为development。在添加特定于测试的rails初始化程序之前,可能值得检查:-)

答案 7 :(得分:1)

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

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

我做到了:

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

它有效:)

答案 8 :(得分:0)

在我作为测试用户登录我的某个应用程序并进行了一些测试上传和测试帖之前,我发生过这种情况。然后我删除了该测试用户,当我尝试使用相同的测试用户再次注册时,我显示了消息“无法找到有效的nil映射”。我所做的就是删除我作为该测试用户所做的所有测试上传和测试帖。然后我再次尝试注册并且有效。通过这种方式,删除内容的更快捷方式是使用db browser for sqlite