こんな人におすすめ
- プログラミング初心者でポートフォリオの作り方が分からない
- Rails Tutorialをやってみたが理解することが難しい
前回:#9 永続セッション, cookie編
次回:#10.5 RSpecでTutorialのテストを書き直す
今回の流れ
- 今回のゴールを把握する
- リメンバーミーのビューを作る
- リメンバーミーのコントローラーを作る
- テストを作る
※ この記事は、ポートフォリオを作る理由をweb系自社開発企業に転職するためとします。
※ 2020年4月6日、記事を大幅に更新しました。
今回のゴールを把握する
ゴールは、自動ログインを選択する、リメンバーミー機能を作ることです。
まずは、リメンバーミーのためのチェックボックスをビューで作ります。
続いて、Sessionsコントローラーを編集します。
最後に、チェックボックスと永続セッションのテストを作ります。
その際、current_userのテストも作ります。
リメンバーミーの完成には、#9を進める必要があります。
まだの方は、#9と合わせてご覧ください。
以上です。
リメンバーミーのビューを作る
リメンバーミーのためのチェックボックスをビューで作ります。
スコープをsession、チェックボックスをremember_meとしています。
よって、params[:session][:remember_me]の形で取得できます。
<% provide(:title, "ログイン") %>
<div class="container form-container login-container">
<div class="row">
<div class="col">
<div class="form-logo-img">
<%= link_to image_tag('lantern_lantern_logo.png', width: 100), root_path, class: "logo-img" %>
</div>
<h1 class="form-title">ログイン</h1>
<%= form_with(scope: :session, url: login_path, local: true) do |form| %>
<div class="form-group">
<%= form.email_field :email, class: 'form-control', placeholder: "メールアドレス" %>
</div>
<div class="form-group">
<%= form.password_field :password, class: 'form-control', placeholder: "パスワード" %>
</div>
<div class="form-group form-check">
<%= form.check_box :remember_me, class: 'form-check-input' %>
<%= form.label :remember_me, '次から保存(ログイン省略)', class: 'form-check-label' %>
</div>
<div class="form-group">
<%= form.submit "ログイン", class: "btn btn-info btn-lg form-submit" %>
</div>
<% end %>
<p class="form-go-to-signup-or-login">新しくはじめる方は<%= link_to "こちら", signup_path %></p>
</div>
</div>
</div>
// 中略
.form-submit {
width: 100%;
// margin-top: 1rem; // 削除
}
リメンバーミーのコントローラーを作る
Sessionsコントローラーを編集します。
params[:session][:remember_me]に保存される値は、オン→'1'、オフ→'0'です。
参考演算子を使って、場合分けします。
class SessionsController < ApplicationController
# 中略
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
params[:session][:remember_me] == '1' ? remember(user) : forget(user)
redirect_to user
else
flash.now[:danger] = 'メールアドレスかパスワードが正しくありません'
render 'new'
end
end
# 中略
テストを作る
リメンバーミーのテストを作ります。
説明の都合上、紹介しなかった#9のテストも作ります。
ここでの手順は、以下の通りです。
- リメンバーミーと永続セッションのテストを作る
- current_userのテストを作る
リメンバーミーと永続セッションのテストを作る
チェックボックスと永続セッションのテストを作ります。
テスト前に、spec内でログインできるローカルメソッドを用意しています。
テストの内容は、以下の通りです。
- 二度ログアウトできたとして、それでもエラーは起こらないか(#9参照)
- チェックボックスがオンの時、トークンが作られているか
- チェックボックスがオフの時、トークンが作られていないか
- チェックボックスがオンの時、ログアウト後のログインでトークンが残っていないか
require 'rails_helper'
RSpec.describe "UsersLogins", type: :request do
include SessionsHelper
let(:user) { create(:user) }
# ログインのメソッド
def post_valid_information(remember_me = 0)
post login_path, params: {
session: {
email: user.email,
password: user.password,
remember_me: remember_me
}
}
end
it "does not log out twice" do
get login_path
post_valid_information(0)
expect(is_logged_in?).to be_truthy
follow_redirect!
expect(request.fullpath).to eq '/users/1'
delete logout_path
expect(is_logged_in?).to be_falsey
follow_redirect!
expect(request.fullpath).to eq '/'
delete logout_path
follow_redirect!
expect(request.fullpath).to eq '/'
end
it "succeeds remember_token because of check remember_me" do
get login_path
post_valid_information(1)
expect(is_logged_in?).to be_truthy
expect(cookies[:remember_token]).not_to be_nil
end
it "has no remember_token because of check remember_me" do
get login_path
post_valid_information(0)
expect(is_logged_in?).to be_truthy
expect(cookies[:remember_token]).to be_nil
end
it "has no remember_token when users logged out and logged in" do
get login_path
post_valid_information(1)
expect(is_logged_in?).to be_truthy
expect(cookies[:remember_token]).not_to be_empty
delete logout_path
expect(is_logged_in?).to be_falsey
expect(cookies[:remember_token]).to be_empty
end
# 中略
end
post_valid_informationメソッドは、引数でチェックボックスを指定します。
マッチャであるbe_nilとbe_emptyを使い分ける理由は、以下の通りです。
- be_nilを使う理由 → be_emptyはnilを判定しないから
- be_emptyを使う理由 → forgetメソッドでcookiesをdeleteする際の中身は、空ではあるが、nilではないから
current_userのテストを作る
current_userのテストを作ります。
テストの内容は、以下の通りです。
- 永続セッションでユーザーを特定できているか
- 不適なトークンをremember_digestに代入した時、current_userはnilか
require 'rails_helper'
RSpec.describe SessionsHelper, type: :helper do
let(:user) { create(:user) }
describe "#current_user" do
it "returns right user when session is nil" do
remember(user)
expect(current_user).to eq user
expect(is_logged_in?).to be_truthy
end
it "returns nil when remember digest is wrong" do
remember(user)
user.update_attribute(:remember_digest, User.digest(User.new_token))
expect(current_user).to be_nil
end
end
end
1つ目のテストの必要性について解説します。
理由は、以下のelsifがテストできていないためです。
わざと例外を発生させるraiseを挿入しても、テストが通ってしまいます。
# 中略
def current_user
if (user_id = session[:user_id])
@current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.signed[:user_id]) # この部分
raise # 追加
user = User.find_by(id: user_id)
if user && user.authenticated?(cookies[:remember_token])
log_in user
@current_user = user
end
end
end
# 中略
そのためのテストでした。
今回は以上です。
参考になりました↓
Rails Tutorial 9.3.1 [Remember me] ボックスをテストする