###パスワードの再設定をテストする
updateに関する4つ条件があった。
送信に成功した場合と失敗した場合の統合テストを作成します。
まずはパスワード再設定のテストファイルを生成しましょう。
ubuntu:~/environment/sample_app (password-reset) $ rails generate integration_test password_resets
Running via Spring preloader in process 2918
invoke test_unit
create test/integration/password_resets_test.rb
####パスワード再設定の統合テスト
test/integration/password_resets_test.rb
require 'test_helper'
class PasswordResetsTest < ActionDispatch::IntegrationTest
def setup
ActionMailer::Base.deliveries.clear
@user = users(:michael)
end
test "password resets" do
get new_password_reset_path
# new_password_reset_pathに行く
assert_template 'password_resets/new'
# newアクションを表示するか?
assert_select 'input[name=?]', 'password_reset[email]'
# input[name=?]タグにpassword_reset[email]が入っているか?
post password_resets_path, params: { password_reset: { email: "" } }
# password_reset_pathに空のメアドを投稿する
assert_not flash.empty?
# flashメッセージは空かどうか?
assert_template 'password_resets/new'
# newアクションを表示する
post password_resets_path,
params: { password_reset: { email: @user.email } }
# メールアドレスが有効
# インスタンス変数をpassword_reset_pathに投稿
assert_not_equal @user.reset_digest, @user.reload.reset_digest
# 多分 前のダイジェストと違うか?
assert_equal 1, ActionMailer::Base.deliveries.size
# 1になるか?
assert_not flash.empty?
assert_redirected_to root_url
#
user = assigns(:user)
# パスワード再設定フォームのテスト
# assigns()を行う
# メールアドレスが無効
get edit_password_reset_path(user.reset_token, email: "")
# emailが空を
assert_redirected_to root_url
# 無効なユーザー
user.toggle!(:activated)
get edit_password_reset_path(user.reset_token, email: user.email)
assert_redirected_to root_url
user.toggle!(:activated)
# メールアドレスが有効で、トークンが無効
get edit_password_reset_path('wrong token', email: user.email)
assert_redirected_to root_url
# メールアドレスもトークンも有効
get edit_password_reset_path(user.reset_token, email: user.email)
# メアドを代入する
assert_template 'password_resets/edit'
# editアクションを表示されているか?
assert_select "input[name=email][type=hidden][value=?]", user.email
# このタグにメアドは書かれているか?
patch password_reset_path(user.reset_token),
params: { email: user.email,
user: { password: "foobaz",
password_confirmation: "barquux" } }
# 無効なパスワードとパスワード確認
# パスワードが違う
assert_select 'div#error_explanation'
patch password_reset_path(user.reset_token),
params: { email: user.email,
user: { password: "",
password_confirmation: "" } }
# パスワードが空
assert_select 'div#error_explanation'
patch password_reset_path(user.reset_token),
params: { email: user.email,
user: { password: "foobaz",
password_confirmation: "foobaz" } }
# 有効なパスワードとパスワード確認
assert is_logged_in?
# ログインされているか?
assert_not flash.empty?
# flashメッセージはからで無いかどうか?
assert_redirected_to user
# それが通ればユーザーページに行っているか?
end
end
#####テスト
ubuntu:~/environment/sample_app (password-reset) $ rails t
Running via Spring preloader in process 10287
Started with run options --seed 16448
46/46: [============================] 100% Time: 00:00:03, Time: 00:00:03
Finished in 3.88961s
46 tests, 211 assertions, 0 failures, 0 errors, 0 skips
###演習
1.
リスト 12.6にあるcreate_reset_digestメソッドはupdate_attributeを2回呼び出していますが、これは各行で1回ずつデータベースへ問い合わせしていることになります。リスト 12.20に記したテンプレートを使って、update_attributeの呼び出しを1回のupdate_columns呼び出しにまとめてみましょう(これでデータベースへの問い合わせが1回で済むようになります)。また、変更後にテストを実行し、 green になることも確認してください。ちなみにリスト 12.20にあるコードには、前章の演習(リスト 11.39)の解答も含まれています。
# パスワード再設定の属性を設定する
def create_reset_digest
self.reset_token = User.new_token
update_columns(reset_digest: User.digest(reset_token),
reset_sent_at: Time.zone.now)
end
リスト 12.21のテンプレートを埋めて、期限切れのパスワード再設定で発生する分岐(リスト 12.16)を統合テストで網羅してみましょう(12.21 のコードにあるresponse.bodyは、そのページのHTML本文をすべて返すメソッドです)。期限切れをテストする方法はいくつかありますが、リスト 12.21でオススメした手法を使えば、レスポンスの本文に「expired」という語があるかどうかでチェックできます(なお、大文字と小文字は区別されません)。
follow_redirect!
# わからない
assert_match "expired", response.body
# 期限切れのパスワード再設定で発生する分岐
end
2時間経ったらパスワードを再設定できなくする方針は、セキュリティ的に好ましいやり方でしょう。しかし、もっと良くする方法はまだあります。例えば、公共の(または共有された)コンピューターでパスワード再設定が行われた場合を考えてみてください。仮にログアウトして離席したとしても、2時間以内であれば、そのコンピューターの履歴からパスワード再設定フォームを表示させ、パスワードを更新してしまうことができてしまいます(しかもそのままログイン機構まで突破されてしまいます!)。この問題を解決するために、リスト 12.22のコードを追加し、パスワードの再設定に成功したらダイジェストをnilになるように変更してみましょう5 。
def update
if params[:user][:password].empty? # (3)への対応
# パスワードが空文字列かどうか?
@user.errors.add(:password, :blank)
# errors.add(:base, '名前の文字数オーバー')
# なのでメッセージを表示する
render 'edit'
# 編集画面に行く。
elsif @user.update(user_params) # (4)への対応
# 新しいパスワードが正しければ、更新する
log_in @user
@user.update_attribute(:reset_digest, nil)
# パスワード再設定が成功したらダイジェストをnilにする
flash[:success] = "Password has been reset."
redirect_to @user
else
render 'edit' # (2)への対応
end
end
リスト 12.18に1行追加し、1つ前の演習課題に対するテストを書いてみましょう。ヒント: リスト 9.25のassert_nilメソッドとリスト 11.33のuser.reloadメソッドを組み合わせて、reset_digest属性を直接テストしてみましょう。
require 'test_helper'
.
.
.
assert_nil user.reload['reset_digest']
end