Description
Environment
- Ruby 3.3.2
- Rails main (8.0.0.alpha)
- Devise 4.9.4
Current behavior
When using the sign_in
Devise test helper method in a controller or integration test, I get the following error:
Error:
HelloControllerTest#test_index:
RuntimeError: Could not find a valid mapping for #<User id: nil, email: nil, created_at: nil, updated_at: nil>
c/tmp/devise/lib/devise/mapping.rb:46:in `find_scope!'
c/tmp/devise/lib/devise/test/integration_helpers.rb:38:in `sign_in'
repro.rb:59:in `test_index'
Here is a minimal reproduction of the issue (run this with ruby repro.rb
):
# repro.rb
require 'bundler/inline'
gemfile do
source 'https://rubygems.org'
gem 'rails', github: 'rails/rails', branch: 'main'
gem 'devise', '~> 4.9.4'
gem 'sqlite3', '~> 1.4'
end
require 'rails'
require 'action_controller/railtie'
require 'active_record'
require 'minitest/autorun'
ENV['RAILS_ENV'] = 'test'
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
ActiveRecord::Schema.define do
create_table :users do |t|
t.string :email, null: false
t.string :encrypted_password, null: true
t.timestamps null: false
end
end
Devise.setup do |config|
require 'devise/orm/active_record'
end
class MyApp < Rails::Application
config.eager_load = false
routes.append do
root to: 'hello#index'
devise_for :users
end
end
class User < ActiveRecord::Base
devise :database_authenticatable
end
MyApp.initialize!
class ApplicationController < ActionController::Base
end
class HelloController < ApplicationController
before_action :authenticate_user!
def index
render plain: 'Hello'
end
end
class HelloControllerTest < ActionDispatch::IntegrationTest
include Devise::Test::IntegrationHelpers
def test_index
sign_in User.new
get '/'
assert_response :success
end
end
Expected behavior
Tests should pass:
.
Finished in 0.023595s, 42.3819 runs/s, 42.3819 assertions/s.
1 runs, 1 assertions, 0 failures, 0 errors, 0 skips
Additional information
Last week deferred route drawing was merged into Rails master to improve boot times for larger apps with lots of routes. Routes are now lazy loaded for Rails environments that have config.eager_load
disabled, which is the case by default for the development and test environments.
Basically by default for the test environment, whenever a route is visited, routes get lazy loaded. Devise's mappings aren't loaded when the test starts, they are only loaded when the test visits a route, because Devise's entry point is defined in the route files with devise_for
or devise_scope
.
But in integration and controller tests you typically call the sign_in
helper before visiting the route. At this point Devise mappings and scopes aren't loaded yet, so Devise will error out.
cc @gmcgibbon
Potential fix: #5695
Activity