何についての記事か
「Railsチュートリアル 第10章 演習 - フレンドリーフォワーディング」の発展学習となります。
同演習には、「渡されたURLに初回のみ転送されていることを確認する。具体的には、リダイレクトのURLはデフォルト (プロフィール画面) に戻っていることを確認する。」という内容の出題があります。それであれば、逆に「『ログインしていない状態でユーザー情報編集ページにアクセスしようとした』という状況で、フレンドリーフォワーディングのリダイレクト先のURLが正しく渡されていること」のテストも必要なはずです。
というわけで、発展学習としてQiita記事を書いてみました。
内容
フレンドリーフォワーディングのリダイレクト先のURLを保存する処理はどこにあるか
現在の実装では、「Usersコントローラーのedit
アクションが呼び出される前には、同コントローラーのlogged_in_user
」メソッドが呼び出されるようになっています。そのコードは以下のとおりです。
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
「ログインしていない状態でUsersコントローラーのedit
アクションが呼び出された」という状況においては、以下のコードが実行されます。
store_location
flash[:danger] = "Please log in."
redirect_to login_url
このうち、フレンドリーフォワーディングのリダイレクト先のURLを一時cookiesに保存するのは、store_location
メソッドですね。当該メソッドは、Sessionsヘルパーに実装されています。
store_location
メソッドの実装
def store_location
session[:forwarding_url] = request.original_url if request.get?
end
「HTTPリクエストがGET
である場合のみ、一時cookiesにフレンドリーフォワーディングのリダイレクト先のURLが保存される」という実装です。
フレンドリーフォワーディングのリダイレクト先のURLを保存する処理に対応するテスト
上述コードに対応するテストコードは、test/controllers/users_controller_test.rb
内にあります。edit
アクションに対応するテストは"should redirect edit when not logged in"です。
test "should redirect edit when not logged in" do
get edit_user_path(@user)
assert_not flash.empty?
assert_redirected_to login_url
end
当該テストに記述を追加すれば、「『ログインしていない状態でユーザー情報編集ページにアクセスしようとした』という状況で、フレンドリーフォワーディングのリダイレクト先のURLが正しく渡されていること」がテストできるはずです。
実際に追加する記述は以下のようになります。
test "should redirect edit when not logged in" do
get edit_user_path(@user)
assert_not flash.empty?
assert_redirected_to login_url
+ assert_not_nil session[:forwarding_url]
end
前提 - テストコードの特定行に対してテストを実行する
例えば、test/controllers/users_controller_test.rb
の15行目に対応するテストを実行するには、rails test
コマンドを以下のように呼び出せばOKです。
# rails test test/controllers/users_controller_test.rb:15
store_location
メソッドが呼び出されない場合に対するテスト
「store_location
メソッドが呼び出されない場合」を再現する
UsersController#logged_in_user
メソッドで、store_location
メソッドが実行されないようにすればOKです。すぐにもとに戻すので、今回は「ソースコードからコメントアウト」という方法をとります。
class UsersController < ApplicationController
before_action :logged_in_user, only: [:edit, :update]
before_action :correct_user, only: [:edit, :update]
...略
private
...略
# beforeアクション
# ログイン済みユーザーかどうか確認
def logged_in_user
unless logged_in?
- store_location
+ # store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
...略
end
「store_location
メソッドが呼び出されない場合」に、edit
アクションに対するテストが失敗することを確認する
edit
アクションに対するテストには、「should redirect edit when not logged in」という名前がついています。私の環境では、テスト「should redirect edit when not logged in」の始まりは、test/controllers/users_controller_test.rb
の15行目となっています。
test "should redirect edit when not logged in" do
この行に対応するテストを実施してみます。
# rails test test/controllers/users_controller_test.rb:15
Running via Spring preloader in process 1220
Started with run options --seed 40280
FAIL["test_should_redirect_edit_when_not_logged_in", UsersControllerTest, 0.5344693000079133]
test_should_redirect_edit_when_not_logged_in#UsersControllerTest (0.54s)
Expected nil to not be nil.
test/controllers/users_controller_test.rb:19:in `block in <class:UsersControllerTest>'
5/5: [===================================] 100% Time: 00:00:00, Time: 00:00:00
Finished in 0.53826s
1 tests, 3 assertions, 1 failures, 0 errors, 0 skips
test/controllers/users_controller_test.rb
の19行目でテストが失敗しましたね。当該行には、以下のコードが記述されています。
assert_not_nil session[:forwarding_url]
たった今追加したコードですね。想定したテストが正しく行えていることがわかりました。
UsersController#logged_in_user
メソッドを元に戻す
テストが正しく実装できていることがわかったので、UsersController#logged_in_user
メソッドは元に戻しておきましょう。
class UsersController < ApplicationController
before_action :logged_in_user, only: [:edit, :update]
before_action :correct_user, only: [:edit, :update]
...略
private
...略
# beforeアクション
# ログイン済みユーザーかどうか確認
def logged_in_user
unless logged_in?
- # store_location
+ store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
...略
end
GET
リクエストに対して、一時cookiesにリダイレクト先のURLが保存されない場合に対するテスト
「GET
リクエストに対して、一時cookiesにリダイレクト先のURLが保存されない場合」を再現する
SessionsHelper#store_location
メソッドの記述を変更します。
module SessionsHelper
...略
# アクセスしようとしたURLを覚えておく
def store_location
- session[:forwarding_url] = request.original_url if request.get?
+ session[:forwarding_url] = request.original_url if !request.get?
end
end
request.get?
を!request.get?
に書き換えることにより、「GET
リクエスト以外のリクエストに対して、一時cookiesにリダイレクト先のURLを保存する」という動作にしています。
「GET
リクエストに対して、一時cookiesにリダイレクト先のURLが保存されない場合」に、edit
アクションに対するテストが失敗することを確認する
# rails test test/controllers/users_controller_test.rb:15
Running via Spring preloader in process 1272
Started with run options --seed 50959
FAIL["test_should_redirect_edit_when_not_logged_in", UsersControllerTest, 0.5778716999921016]
test_should_redirect_edit_when_not_logged_in#UsersControllerTest (0.58s)
Expected nil to not be nil.
test/controllers/users_controller_test.rb:19:in `block in <class:UsersControllerTest>'
5/5: [===================================] 100% Time: 00:00:00, Time: 00:00:00
Finished in 0.58515s
1 tests, 3 assertions, 1 failures, 0 errors, 0 skips
先ほどと同じく、test/controllers/users_controller_test.rb
の19行目でテストが失敗しました。想定したテストが正しく行えているといえます。
SessionsHelper#store_location
メソッドを元に戻す
テストが正しく実装できていることがわかったので、SessionsHelper#store_location
メソッドは元に戻しておきましょう。
module SessionsHelper
...略
# アクセスしようとしたURLを覚えておく
def store_location
- session[:forwarding_url] = request.original_url if !request.get?
+ session[:forwarding_url] = request.original_url if request.get?
end
end
実装が正しいことの確認
改めて、test/controllers/users_controller_test.rb
の15行目に対応するテストを実行します。
# rails test test/controllers/users_controller_test.rb:15
Running via Spring preloader in process 1298
Started with run options --seed 23019
5/5: [===================================] 100% Time: 00:00:00, Time: 00:00:00
Finished in 0.66698s
1 tests, 3 assertions, 0 failures, 0 errors, 0 skips
テストは無事成功しました。