前提
Railsチュートリアル 第12章のうち、PasswordResetsコントローラーのedit
アクションの動作を、テスト駆動で実装していったところです。以下のようなテストが、失敗なしで一気に成功してしまいました。このままでは、どのテストが何に対してのものなのかがわからないですね。
# メールアドレスが無効
get edit_password_reset_path(user.reset_token, 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'
assert_select "input[name=email][type=hidden][value=?]", user.email
どのような実装抜けがあったら、どのテストが失敗するのでしょうか。
無効なメールアドレスがチェックされない場合
PasswordResetsコントローラーのvalid_user
の実装が以下のようになっていたらどうなるでしょうか。
class PasswordResetsController < ApplicationController
before_action :get_user, only: [:edit, :update]
before_action :valid_user, only: [:edit, :update]
# ...略
private
def get_user
@user = User.find_by(email: params[:email])
end
# 正しいユーザーかどうか確認する
def valid_user
unless (@user.activated? &&
@user.authenticated?(:reset, params[:id]))
redirect_to root_url
end
end
end
# rails test test/integration/password_resets_test.rb
Running via Spring preloader in process 762
Started with run options --seed 53179
ERROR["test_password_resets", PasswordResetsTest, 4.072652899998502]
test_password_resets#PasswordResetsTest (4.07s)
NoMethodError: NoMethodError: undefined method `activated?' for nil:NilClass
app/controllers/password_resets_controller.rb:32:in `valid_user'
test/integration/password_resets_test.rb:28:in `block in <class:PasswordResetsTest>'
1/1: [===================================] 100% Time: 00:00:04, Time: 00:00:04
Finished in 4.07946s
1 tests, 8 assertions, 0 failures, 1 errors, 0 skips
私の環境では、test/integration/password_resets_test.rb
の28行目は以下のコードが記述されています。
get edit_password_reset_path(user.reset_token, email: "")
email属性として空文字列を与えた場合に、「activated?
メソッドが定義されていない」というエラーが発生している、ということのようです。
email属性として空文字列を与えた場合、@user
の状態はどうなるか
「activated?
メソッドが定義されていない」というエラーが発生する直前にdebugger
を入れると、結果はどうなるでしょうか。
class PasswordResetsController < ApplicationController
before_action :get_user, only: [:edit, :update]
before_action :valid_user, only: [:edit, :update]
...略
private
def get_user
@user = User.find_by(email: params[:email])
end
# 正しいユーザーかどうか確認する
def valid_user
+ debugger
unless (@user.activated? &&
@user.authenticated?(:reset, params[:id]))
redirect_to root_url
end
end
end
(byebug) @user
nil
「無効なメールアドレスをクエリパラメータとしてUser.find_by
メソッドが呼び出されたため、@user
がnil
になってしまっている」ということのようです。
有効化されていないユーザーがチェックされない場合
PasswordResetsコントローラーのvalid_user
の実装が以下のようになっていたらどうなるでしょうか。
class PasswordResetsController < ApplicationController
before_action :get_user, only: [:edit, :update]
before_action :valid_user, only: [:edit, :update]
# ...略
private
def get_user
@user = User.find_by(email: params[:email])
end
# 正しいユーザーかどうか確認する
def valid_user
unless (@user &&
@user.authenticated?(:reset, params[:id]))
redirect_to root_url
end
end
end
test/integration/password_resets_test.rb
に対するテストの結果は以下のようになります。
# rails test test/integration/password_resets_test.rb
Running via Spring preloader in process 788
Started with run options --seed 20070
FAIL["test_password_resets", PasswordResetsTest, 3.081338899995899]
test_password_resets#PasswordResetsTest (3.08s)
Expected response to be a <3XX: redirect>, but was a <200: OK>
test/integration/password_resets_test.rb:33:in `block in <class:PasswordResetsTest>'
1/1: [===================================] 100% Time: 00:00:03, Time: 00:00:03
Finished in 3.10585s
1 tests, 10 assertions, 1 failures, 0 errors, 0 skips
私の環境では、test/integration/password_resets_test.rb
の30行目から33行目には以下のコードが記述されています。
# 無効なユーザー
user.toggle!(:activated)
get edit_password_reset_path(user.reset_token, email: user.email)
assert_redirected_to root_url
メッセージの内容は以下です。
Expected response to be a <3XX: redirect>, but was a <200: OK>
ということは、「ユーザーが有効化されていない場合に、リダイレクトされるべきところがリダイレクトされていない」という理由でのテストの失敗のようですね。
パスワード再設定用トークンの有効性がチェックされない場合
PasswordResetsコントローラーのvalid_user
の実装が以下のようになっていたらどうなるでしょうか。
class PasswordResetsController < ApplicationController
before_action :get_user, only: [:edit, :update]
before_action :valid_user, only: [:edit, :update]
# ...略
private
def get_user
@user = User.find_by(email: params[:email])
end
# 正しいユーザーかどうか確認する
def valid_user
unless (@user && @user.activated?)
redirect_to root_url
end
end
end
test/integration/password_resets_test.rb
に対するテストの結果は以下のようになります。
# rails test test/integration/password_resets_test.rb
Running via Spring preloader in process 801
Started with run options --seed 55315
FAIL["test_password_resets", PasswordResetsTest, 3.1079486000016914]
test_password_resets#PasswordResetsTest (3.11s)
Expected response to be a <3XX: redirect>, but was a <200: OK>
test/integration/password_resets_test.rb:37:in `block in <class:PasswordResetsTest>'
1/1: [===================================] 100% Time: 00:00:03, Time: 00:00:03
Finished in 3.11229s
1 tests, 11 assertions, 1 failures, 0 errors, 0 skips
私の環境では、test/integration/password_resets_test.rb
の35行目から37行目には以下のコードが記述されています。
# メールアドレスが有効で、トークンが無効
get edit_password_reset_path('wrong token', email: user.email)
assert_redirected_to root_url
メッセージの内容は以下です。
Expected response to be a <3XX: redirect>, but was a <200: OK>
ということは、「パスワード再設定用トークンが有効でない場合に、リダイレクトされるべきところがリダイレクトされていない」という理由でのテストの失敗のようですね。