'Rspec and Devise: Could not find a valid mapping for #<User

using RSpec and Devise, I'm getting a mapping error.

  1) BusinessesController GET index sets @new_vacation
     Failure/Error: sign_in @user
     RuntimeError:
       Could not find a valid mapping for #<User id: 8009, email: "[email protected]", encrypted_password: "$2a$10$joN9Vy6spNAe4uh7W9fcOOdoeSJ5dr/nBwpmlqcv6EYe...", reset_password_token: nil, remember_token: nil, remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, created_at: "2011-04-01 17:05:03", updated_at: "2011-04-01 17:05:03", business_id: 7214, confirmation_token: nil, confirmed_at: "2011-08-24 21:23:51", confirmation_sent_at: nil, failed_attempts: 0, unlock_token: nil, locked_at: nil, admin: true, password_salt: nil>
     # ./spec/controllers/businesses_controller_spec.rb:10:in `block (2 levels) in <top (required)>'

It looks like the answer might be in here RSpec tests with devise: could not find valid mapping but I can't get it.

Any ideas, I'm really stumped! Thanks.


routes.rb

  constraints(SubdomainRoute) do

    # SIGN IN
    devise_for :accounts, :controllers => {
                                                      :sessions => "users/accounts/sessions",
                                                      :confirmations =>  "users/accounts/confirmations" },
                                                      :class_name => 'Admin' do
      get "accounts/sign_in", :to => "users/accounts/sessions#new"

      get "accounts/sign_up", :to => "users/devise/registrations#new"
      get "accounts/signedup", :to => "users/devise/confirmations#signedup", :as => "signedup_registration"
    end
end

admin.rb

class Admin < User
  validates_associated :business
end

user.rb

class User < ActiveRecord::Base
  belongs_to :business
  accepts_nested_attributes_for :business

  # TODO get lockable working -- failed_attempts is incrementing in db
  devise :database_authenticatable, :registerable, :confirmable, :lockable,
         :recoverable, :rememberable, :trackable, :validatable
end

factories.rb

FactoryGirl.define do

  Factory.define :business do |b|
  end

  Factory.define :business_main, :parent => :business do |b|
    b.business_name "Ann's Salon"
    b.address_line1 "123 Main Street"
    b.address_line2 ""
    b.city "Portland"
    b.state "Oregon"
    b.zip "97211"
  end

  factory :admin do
    confirmed_at Time.now

    factory :admin_one do
      association :business, :factory => :business_one
      email
      password 'please'
      admin true
    end
  end
end

business_controller_spec.rb

require 'spec_helper'

describe BusinessesController do

  before (:each) do
    @business = Factory(:business_one)
    @user     = User.find_by_business_id(@business.id)
    sign_in @user
  end

  describe "GET index" do

    let(:describe_action) { get :index }

    it "sets @new_vacation" do
      BusinessVacation.should_receive(:new)
      get :index
    end
  end

end


Solution 1:[1]

I had this error in my routes file because I had an API endpoint that I wanted to allow users to reset their passwords from, but I wanted the users to actually do the password change on the web view, which wasn't namespaced under /api

This is how I fixed the routes to make it work:

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

Solution 2:[2]

First make sure you have called both 'devise_for' in your routes and 'devise' in your model. I recommend you to read the documentation in the README and in the wiki for more information.

If yes try this:

Put this code into your 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    

This is because Devise uses a mapping between classes and routes, so when a factory built object comes through to Devise after a console reload, or a class redefinition then it will fail. This is commonly the case in development and test environments.

I found it here & worked fine for me- http://blog.thefrontiergroup.com.au/2011/03/reloading-factory-girl-factories-in-the-rails-3-console/

Solution 3:[3]

You may want to check your code for multiple devise_for :users declarations in different places. This has been the cause of such an exception in my case, as it surely confused Devise.

Solution 4:[4]

I got this in my spec from merely trying to create a model (User.create or factorybot: create(:user)). For me it had something to do with the confirmable devise module.

The fix for me was setting confirmed_at, confirmation_sent_at, etc. on the model (which were sensible defaults for my test) and then the error went away.

FactoryBot.define do
  factory :user do
    sequence(:first_name) { |n| "user_first_name_#{n}" }
    # ...
    confirmed_at { Time.zone.now - 1.hour }
    confirmation_sent_at { Time.zone.now - 1.hour }
    confirmation_token { "abcd1234" }
  end
end

Solution 5:[5]

My problem was unrelated to this code. I had recently added a before-filter to always redirect to www.

redirect_to request.protocol + "www." + request.host_with_port + request.fullpath if !/^www/.match(request.host)

I'm still not totally sure why this caused my problem, but it did.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Neal
Solution 2
Solution 3 mxgrn
Solution 4 dechimp
Solution 5 Undo