起こったこと
- deviseの
registrations#new
をテストしたい -
registrations#new
には管理者のみアクセス可能にして、それ以外のユーザーはルートにリダイレクトしたい - しかし、下記のエラーが出てうまく行かない
NoMethodError: undefined method `name' for nil:NilClass
from <hoge_hoge>/vendor/bundle/ruby/2.3.0/gems/devise-4.2.0/app/controllers/devise_controller.rb:42:in `resource_name'
- テストコード
registrations_controller_spec.rb
require 'rails_helper'
RSpec.describe Admin::Devises::RegistrationsController, type: :controller do
describe 'GET #new' do
let (:response) { get :new }
context '管理者の場合' do
login_admin
it 'ステータス 200 になる' do
expect(response).to be_success
end
end
context '管理者以外の場合' do
login_writer
it 'ステータス 300番台でホーム画面にリダイレクト' do
expect(response).to be_redirect
expect(response).to redirect_to root_path
end
end
end
end
エラーの原因
- まずエラーが起きている場所は下記のソース
devise_controller.rb
# Proxy to devise map name
def resource_name
devise_mapping.name
end
devise_mapping.name
がnil
なのでどこかで設定をミスっているdeviseの wiki にヒントがあった
Every time you want to unit test a devise controller, you need to tell Devise which mapping to use. We need that because ActionController::TestCase and spec/controllers bypass the router and it is the router that tells Devise which resource is currently being accessed, you can do that with:
-
devise_controller.rb
をユニットテストするたびに使用するマッピングをDevise
に伝える必要がある - その設定は wiki の通りに
controller_macros.rb
に書いて上記のregistrations_controller_spec.rb
で呼んでいる
controller_macros.rb
module ControllerMacros
def login_admin
before(:each) do
@request.env["devise.mapping"] = Devise.mappings[:admin]
sign_in FactoryGirl.create(:admin)
end
end
def login_writer
before(:each) do
@request.env["devise.mapping"] = Devise.mappings[:admin]
sign_in FactoryGirl.create(:writer)
end
end
end
-
Devise.mappings[:admin]
でマッピングするシンボルはregistrations#new
のルーティングがnamespace
などで変更されている場合、それに合わせて書き換えないといけない - 自分に場合、下記のようなルーティングにしていた
routes.rb
<省略>
namespace :admin do
devise_for :users, controllers: {
sessions: 'admin/devises/sessions',
registrations: 'admin/devises/registrations',
passwords: 'admin/devises/passwords',
}
<省略>
-
namespace :admin
でマッピングするシンボルが変わるので、正しくは下記のようなcontroller_macros.rb
になる
controller_macros.rb
<省略>
@request.env["devise.mapping"] = Devise.mappings[:admin_user]
<省略>
@request.env["devise.mapping"] = Devise.mappings[:admin_user]
<省略>
- とても長い時間ハマっていたので、誰かのお役に立てば良い