【Railsチュートリアル】リスト10.29のテストをより詳細に書いてみる

# 目的
Railsチュートリアル10.2.3章では「フレンドリーフォワーディング」について学んでいきます。
https://railstutorial.jp/chapters/updating_and_deleting_users?version=6.0#sec-friendly_forwarding
ただこの章、初学者にとって理解しにくい箇所だと思ったので自分のためにもまとめておきます。
最終的にはリスト10.29のテストをより詳細にし、ふりがなを振っていきます。

# 前提
リスト10.33まで進め、リスト10.29のテストコードがGREENになる状態にしておいてください。

# 解説
## そもそもこのテストで何をしようとしているのか？

リスト10.29にふりがなを振っていきます。

```ruby:test/integration/users_edit_test.rb
require 'test_helper'

class UsersEditTest < ActionDispatch::IntegrationTest

  # @userにfixturesで定義したmichaelを代入
  def setup
    @user = users(:michael)
  end
  .
  .
  .
  test "successful edit with friendly forwarding" do
    # ログイン前にmichaelのユーザー編集ページにアクセス
    get edit_user_path(@user)
    # users_cotrollerのbeforeactionで設定されたlogged_in_userによりログイン画面に飛ばされる
    # このとき、アクセスしようとしていた/users/:id/editのurlがstore_locationメソッドによりsession[:forwarding_url]に保存される
    # flashが表示される
    # @userとしてログインする
    log_in_as(@user)
    # redirect_back_or(default)メソッドにより@userのユーザー編集画面にリダイレクトする
    assert_redirected_to edit_user_url(@user)
    # redirect_back_orメソッドによりforwarding_urlは破棄される
    # nameとemailを定義
    name  = "Foo Bar"
    email = "foo@bar.com"
    # paramsに下記情報を入れ、ユーザー情報を編集
    patch user_path(@user), params: { user: { name:  name,
                                              email: email,
                                              password:              "",
                                              password_confirmation: "" } }
    # ユーザー情報が編集されたことを表すflashが表示される
    assert_not flash.empty?
    # @userのページにリダイレクトされる
    assert_redirected_to @user
    # @userの中身を確認
    @user.reload
    # @user.nameが上で定義したnameに更新されている
    assert_equal name,  @user.name
    # @user.emailが上で定義したemailに更新されている
    assert_equal email, @user.email
  end
end
```

ということになります。
`get edit_user_path(@user)`と`log_in_as(@user)`の間、結構省略されてると思いませんか？
ここを詳細に見ていきます。

## logged_in_userでは何が行われているのか？
`get edit_user_path(@user)`のあと、logged_in_userがおこなわれます。
users_controllerで設定されたlogged_in_userの中身を見ていきましょう。
```ruby
# ログイン済みユーザーかどうか確認
def logged_in_user
  # sessions_helperで定義したlogged_in?メソッドがfalse(ログインしていない)とき
  unless logged_in?
    # sessions_helperで定義したstore_locationをおこなう
    store_location
    # flashを表示させる
    flash[:danger] = "Please log in."
    # ログイン画面にリダイレクトする
    redirect_to login_url
  end
end
```

ここで、store_locationというメソッドがでてきました。
この中身も確認しましょう。

```ruby
# アクセスしようとしたURLを覚えておく
def store_location
  # getリクエストが送られたとき、そのときのurlをsession[:forwarding_url]に保存する
  session[:forwarding_url] = request.original_url if request.get?
end
```

以上を踏まえると、
**ログインしていない状態でusers_controllerのlogged_in_userが設定されたurlにアクセスすると**
**そのurlを保存し、ログイン画面に移る。**
**ログイン後は保存したurlに飛ぶ。**
ということになります。

## redirect_back_or(default)では何がおこなわれているのか？
`log_in_as(@user)`のあと、sessions_controllerのcreateアクション内のredirect_back_or(default)メソッドが実行されます。
redirect_back_or(default)メソッドの中身を見ていきましょう。
このメソッドはsessions_helperにて定義されています。
```ruby
# 記憶したURL(もしくはデフォルト値)にリダイレクト
def redirect_back_or(default)
  # 保存したurlもしくはdefaultのurlにリダイレクトする
  redirect_to(session[:forwarding_url] || default)
  # 保存したurlを削除する
  session.delete(:forwarding_url)
end
```

まとめると、
**ユーザー編集ページにて情報を送信したあとのリダイレクト先は、**
**session[:forwarding_url]があるなら保存されたurl、なければデフォルトのurlである。**
**このとき、session[:forwarding_url]は削除される。**
ということになります。

# リスト10.29をより詳細に書いてみよう
さて、ログイン前にユーザー編集画面にアクセスしたときの挙動が分かったところで、リスト10.29をより詳細に書いてみましょう。
詳細に書くことで挙動の理解を深め、定義したメソッドが正しく動いているかも同時にテストすることができます。

```ruby:test/integration/users_edit_test.rb
require 'test_helper'

class UsersEditTest < ActionDispatch::IntegrationTest

  # @userにfixturesで定義したmichaelを代入
  def setup
    @user = users(:michael)
  end
  .
  .
  .
  test "successful edit with friendly forwarding" do
    # ログイン前にmichaelのユーザー編集ページにアクセス
    get edit_user_path(@user)
    # users_cotrollerのbeforeactionで設定されたlogged_in_userによりログイン画面に飛ばされる
    assert_redirected_to login_url
    # このとき、アクセスしようとしていた/users/:id/editのurlがstore_locationメソッドによりsession[:forwarding_url]に保存される
    assert_not session[:forwarding_url].nil?
    # flashが表示される
    assert_not flash.empty?
    # @userとしてログインする
    log_in_as(@user)
    # redirect_back_or(default)メソッドにより@userのユーザー編集画面にリダイレクトする
    assert_redirected_to edit_user_url(@user)
    # redirect_back_orメソッドによりforwarding_urlは破棄される
    assert session[:forwarding_url].nil?
    # nameとemailを定義
    name  = "Foo Bar"
    email = "foo@bar.com"
    # paramsに下記情報を入れ、ユーザー情報を編集
    patch user_path(@user), params: { user: { name:  name,
                                              email: email,
                                              password:              "",
                                              password_confirmation: "" } }
    # ユーザー情報が編集されたことを表すflashが表示される
    assert_not flash.empty?
    # @userのページにリダイレクトされる
    assert_redirected_to @user
    # @userの中身を確認
    @user.reload
    # @user.nameが上で定義したnameに更新されている
    assert_equal name,  @user.name
    # @user.emailが上で定義したemailに更新されている
    assert_equal email, @user.email
  end
end
```

このテストはGREENになります。

# まとめ
テストの中身をより詳細に記載しました。
あるアクションを実行したときの挙動を自分の言葉で言語化できるようになるとより理解が深まりますね。