I'm setting up devise for use in a rails 4 app with single table inheritance on the user model. We have 7 different classes of user, each with their own ActiveRecord model that inherits from a base User
model.
I've worked through what I could find on the web, but I'm having a lot of weird problems around getting my tests set up correctly. I wrote a custom sessions_controller to figure out which type of user is trying to log in and signs in the correct resource class, and I wrote a little spec to verify it's working while I get the rest of the feature up and running. I can't seem to get the correct methods stubbed to get devise to work with rspec. I first kept running into the error:
Failure/Error: post :create, user: { email: 'fake@address.com' }
NoMethodError:
undefined method `authenticate?' for nil:NilClass
which is weird because I include both devise and warden test helpers in my spec helper file (pasted below). When I also include Devise::TestHelpers in the SessionsController spec directly I get a different error however:
1) SessionsController POST create works
Failure/Error: post :create, user: { email: 'fake@address.com' }
ArgumentError:
uncaught throw :warden
# ./app/controllers/sessions_controller.rb:5:in 'create'
# ./spec/controllers/sessions_controller_spec.rb:17:in 'block (3 levels) in <top (required)>'
I've done a ton of googling and only found suggestions around adding the devise and warden test helpers to the spec files or setting the "devise.mapping" on @request.env (both of which i've already done). Any and all help will be appreciated, especially a pointer to a tutorial on the web that I haven't seen ;). I've looked at these pages:
- http://adamrobbie.me/blog/2013-3-29-sti-with-rails-40-beta-and-devise,
- Rails: Using Devise with single table inheritance,
- Using Devise for Two Different Models but the same Login Form,
- Devise - single sign in form for multiple users,
- devise and multiple "user" models
here is my sessions_controller.rb:
class SessionsController < Devise::SessionsController
class UnrecognizedUserTypeError < StandardError; end
def create
created = super do
sign_in(type, user_class.try(:find, resource.id))
end
created
end
private
def type
unless User::TYPES.include(type = resource.type.try(:underscore))
raise UnrecognizedUserTypeError
end
type
end
def user_class
type.try(:constantize)
end
end
here is my sessions_controller_spec.rb:
require 'rails_helper'
describe SessionsController do
# This line should not be needed since it has been included in spec_helper.rb
# but this spec file doesn't seem to work without it (bl 2014-08-11).
include Devise::TestHelpers
before do
setup_controller_for_warden
# Since we're bypassing the router in this test, we need to explicitly
# tell devise which mapping to use.
@request.env["devise.mapping"] = Devise.mappings[:user]
end
describe "POST create" do
it "works" do
post :create
expect(response.code).to eq(200)
end
end
end
and here are the relevant lines from my spec_helper.rb (which rails_helper requires)
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
config.include Devise::TestHelpers, type: :controller
config.include Warden::Test::Helpers
config.before(:suite) do
begin
DatabaseCleaner.start
FactoryGirl.lint
Warden.test_mode!
ensure
DatabaseCleaner.clean
end
end
config.after(:suite) do
Warden.test_reset!
end
...
end
Here are the relevant lines from config/routes.rb:
Rails.application.routes.draw do
devise_for :users, controllers: { sessions: 'sessions' }
devise_for :administrator_users, skip: :sessions
devise_for :manager_users, skip: :sessions
devise_for :adjuster_users, skip: :sessions
devise_for :nurse_case_manager_users, skip: :sessions
devise_for :technical_assistant_users, skip: :sessions
devise_for :provider_users, skip: :sessions
devise_for :super_administrator_users, skip: :sessions
...
end