前回の環境構築(Macでrails newしてrails sで動かすまで)の続きとして、
やっぱりユーザー認証が必須の場合が多いので、deviseを極力シンプルに導入してみます。
Rails6。色々、変わってるのでメモしておきます。
deviseインストール
Gemfileの最終行に追加
# Use devise gem 'devise'
$ bundle install $ rails g devise:install
config/environments/development.rbの39行目辺りに追加
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
app/views/layouts/application.html.erbの13行目辺り(Bodyの中)に追加
<p class="notice"><%= notice %></p> <p class="alert"><%= alert %></p>
$ rails g devise user $ rails g devise:views users $ rails g devise:controllers users
config/routes.rbの2行目辺りのを変更
devise_for :users ↓ devise_for :users, controllers: { sessions: 'users/sessions' }
$ rails db:migrate
$ rails routes Prefix Verb URI Pattern Controller#Action new_user_session GET /users/sign_in(.:format) devise/sessions#new user_session POST /users/sign_in(.:format) devise/sessions#create destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy new_user_password GET /users/password/new(.:format) devise/passwords#new edit_user_password GET /users/password/edit(.:format) devise/passwords#edit user_password PATCH /users/password(.:format) devise/passwords#update PUT /users/password(.:format) devise/passwords#update POST /users/password(.:format) devise/passwords#create cancel_user_registration GET /users/cancel(.:format) devise/registrations#cancel new_user_registration GET /users/sign_up(.:format) devise/registrations#new edit_user_registration GET /users/edit(.:format) devise/registrations#edit user_registration PATCH /users(.:format) devise/registrations#update PUT /users(.:format) devise/registrations#update DELETE /users(.:format) devise/registrations#destroy POST /users(.:format) devise/registrations#create
$ rails s -> http://localhost:3000/users/sign_up -> http://localhost:3000/users/edit
【参考】ここまでのコミット内容
https://dev.azure.com/nightonly/rails-app-origin/_git/rails-app-origin/commit/7dabcb0ac075721fdee3a0658111080bf34cc3ea
動線追加
app/views/layouts/application.html.erbの13行目辺り(Bodyの中)に追加
<ul> <% if user_signed_in? %> <li><%= current_user.email %></li> <li><%= link_to 'Edit User', edit_user_registration_path %></li> <li><%= link_to 'Log out', destroy_user_session_path, method: :delete %></li> <% else %> <li><%= link_to 'Log in', new_user_session_path %></li> <li><%= link_to 'Sign up', new_user_registration_path %></li> <% end %> </ul> <hr/>
RSpec対応
$ rspec Failures: 1) TopController GET #index returns http success Failure/Error: <% if user_signed_in? %> ActionView::Template::Error: Devise could not find the `Warden::Proxy` instance on your request environment. Make sure that your application is loading Devise and Warden as expected and that the `Warden::Manager` middleware is present in your middleware stack. If you are seeing this on one of your tests, ensure that your tests are either executing the Rails middleware stack or that your tests are using the `Devise::Test::ControllerHelpers` module to inject the `request.env['warden']` object for you.
spec/rails_helper.rbの2712行目辺りに追加
# Use devise require 'devise'
66行目辺り(endの前)に追加
# Use devise config.include Devise::Test::ControllerHelpers, type: :controller config.include Devise::Test::ControllerHelpers, type: :view
$ rspec 2 examples, 0 failures
通った。
RSpec追加
追加した動線やメールアドレスが含まれるかのテストを追加してみます。
Gemfileのrspec-railsの34行目辺り(RSpecの所)に足りないgemがあれば追加
# Use RSpec gem 'rspec-rails', '>= 4.0.0' # Tips: https://qiita.com/amatsukix/items/578f85cf4565ca2a797c gem 'spring-commands-rspec' gem 'rails-controller-testing' gem 'factory_bot_rails' gem 'faker'
$ bundle install
spec/rails_helper.rbの2712行目辺りに追加
# Use devise require 'devise'require File.expand_path('spec/support/controller_macros.rb')
28行目辺りの変更
# Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } ↓ Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f }
7170行目辺り(endの前)に追加
config.include ControllerMacros, type: :controller config.include ControllerMacros, type: :view config.include FactoryBot::Syntax::Methods
spec/support/controller_macros.rbを作成
module ControllerMacros def login_user(user) allow(controller).to receive(:authenticate_user!).and_return(true) @request.env['devise.mapping'] = Devise.mappings[:user] sign_in user end end
spec/factories/users.rbを作成
FactoryBot.define do factory :user do pass = Faker::Internet.password(min_length: 8) email { Faker::Internet.email } password { pass } password_confirmation { pass } end end
spec/views/layouts/application.html.erb_spec.rbを作成
require 'rails_helper' RSpec.describe 'layouts/application', type: :view do let!(:user) { create(:user) } context '未ログイン' do it 'Log inのパスが含まれる' do renderexpect(rendered).to match(new_user_session_path)# これだとURLが他のURLの一部に含まれる場合に意図せず成功してしまうので修正 # 例:/spaces/editが存在すると、/spacesがあると誤認expect(rendered).to match("\"#{Regexp.escape(new_user_session_path)}\"")# こっちの方がシンプルですね。 expect(rendered).to include("\"#{new_user_session_path}\"") end it 'Sign upのパスが含まれる' do renderexpect(rendered).to match(new_user_registration_path)expect(rendered).to match("\"#{Regexp.escape(new_user_registration_path)}\"")expect(rendered).to include("\"#{new_user_registration_path}\"") end end context 'ログイン中' do before do login_user user end it 'ログインユーザーのメールアドレスが含まれる' do renderexpect(rendered).to match(user.email)expect(rendered).to include(user.email) end it 'Edit Userのパスが含まれる' do renderexpect(rendered).to match(edit_user_registration_path)expect(rendered).to match("\"#{Regexp.escape(edit_user_registration_path)}\"")expect(rendered).to include("\"#{edit_user_registration_path}\"") end it 'Log outのパスが含まれる' do renderexpect(rendered).to match(destroy_user_session_path)expect(rendered).to match("\"#{Regexp.escape(destroy_user_session_path)}\"")expect(rendered).to include("\"#{destroy_user_session_path}\"") end end end
$ rspec 7 examples, 0 failures
deviceのroutingテスト追加
パス(URLの一部)と呼び出されるcontrollerの対応(config/routes.rb)が正しいかのテストだと理解。
リリース後に意図せずURLが変わるとリンク切れ等、トラブルになるのであった方が良さそう。
また、requestのテストをしなくても、以降はcontrollerのテストで担保できると。
spec/routing/users/registrations_routing_spec.rbを作成
require 'rails_helper' RSpec.describe Users::RegistrationsController, type: :routing do describe 'routing' do it 'routes to #new' do expect(get: '/users/sign_up').to route_to('devise/registrations#new') end it 'routes to #create' do expect(post: '/users').to route_to('devise/registrations#create') end it 'routes to #edit' do expect(get: '/users/edit').to route_to('devise/registrations#edit') end it 'routes to #update' do expect(put: '/users').to route_to('devise/registrations#update') end it 'routes to #destroy' do expect(delete: '/users').to route_to('devise/registrations#destroy') end it 'routes to #cancel' do expect(get: '/users/cancel').to route_to('devise/registrations#cancel') end end end
spec/routing/users/sessions_routing_spec.rbを作成
require 'rails_helper' RSpec.describe Users::SessionsController, type: :routing do describe 'routing' do it 'routes to #new' do expect(get: '/users/sign_in').to route_to('users/sessions#new') end it 'routes to #create' do expect(post: '/users/sign_in').to route_to('users/sessions#create') end it 'routes to #destroy' do expect(delete: '/users/sign_out').to route_to('users/sessions#destroy') end end end
spec/routing/users/passwords_routing_spec.rbを作成
require 'rails_helper' RSpec.describe Users::PasswordsController, type: :routing do describe 'routing' do it 'routes to #new' do expect(get: '/users/password/new').to route_to('devise/passwords#new') end it 'routes to #new' do expect(post: '/users/password').to route_to('devise/passwords#create') end it 'routes to #edit' do expect(get: '/users/password/edit').to route_to('devise/passwords#edit') end it 'routes to #update' do expect(put: '/users/password').to route_to('devise/passwords#update') end end end
$ rspec 20 examples, 0 failures
deviceの機能拡張は他を他サイトを参考にして頂ければと思いますが、
今回未使用の下記に対応するテストも必要に応じて追加した方が良さそうですね。
app/controllers/users/confirmations_controller.rb
app/controllers/users/omniauth_callbacks_controller.rb
app/controllers/users/unlocks_controller.rb
【参考】ここまでのコミット内容
https://dev.azure.com/nightonly/rails-app-origin/_git/rails-app-origin/commit/38551c3c9a7cd08eb59af89698e7cccc0235a46e
“Railsアプリにサクッとdeviseを導入” に対して3件のコメントがあります。