0
0

More than 1 year has passed since last update.

railsチュートリアル第12章 パスワードの再設定をテストする

Posted at

パスワードの再設定をテストする

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

2.
リスト 12.21のテンプレートを埋めて、期限切れのパスワード再設定で発生する分岐(リスト 12.16)を統合テストで網羅してみましょう(12.21 のコードにあるresponse.bodyは、そのページのHTML本文をすべて返すメソッドです)。期限切れをテストする方法はいくつかありますが、リスト 12.21でオススメした手法を使えば、レスポンスの本文に「expired」という語があるかどうかでチェックできます(なお、大文字と小文字は区別されません)。

 follow_redirect!
    # わからない
    assert_match "expired", response.body
    # 期限切れのパスワード再設定で発生する分岐
  end

3.
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

4.
リスト 12.18に1行追加し、1つ前の演習課題に対するテストを書いてみましょう。ヒント: リスト 9.25のassert_nilメソッドとリスト 11.33のuser.reloadメソッドを組み合わせて、reset_digest属性を直接テストしてみましょう。

require 'test_helper'
.
.
.
  assert_nil user.reload['reset_digest']
end
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0