何について書いているか
「Railsチュートリアル 第12章 パスワードの再設定」の「create
アクションでパスワード再設定」のうち、メールアドレスが無効な場合の実装を、テスト駆動で行っていこうという内容です1。
メールアドレスが無効な場合の処理に対するテストの実装
Railsチュートリアル本文の通りに実装していくとすれば、以下のテストが「メールアドレスが無効な場合の処理に対するテスト」に該当することになります。
require 'test_helper'
class PasswordResetsTest < ActionDispatch::IntegrationTest
def setup
ActionMailer::Base.deliveries.clear
@user = users(:rhakurei)
end
test "password resets" do
+ get new_password_reset_path
+ assert_template 'password_resets/new'
+ # メールアドレスが無効
+ post password_resets_path, params: { password_reset: { email: "" } }
+ assert_not flash.empty?
+ assert_template 'password_resets/new'
end
end
現時点でテストが失敗することの確認
# rails test test/integration/password_resets_test.rb
Running via Spring preloader in process 77
Started with run options --seed 17171
ERROR["test_password_resets", PasswordResetsTest, 2.4794358999999986]
test_password_resets#PasswordResetsTest (2.48s)
AbstractController::ActionNotFound: AbstractController::ActionNotFound: The action 'create' could not be found for PasswordResetsController
test/integration/password_resets_test.rb:14:in `block in <class:PasswordResetsTest>'
1/1: [===================================] 100% Time: 00:00:02, Time: 00:00:02
Finished in 2.48535s
1 tests, 1 assertions, 0 failures, 1 errors, 0 skips
現時点でテストが失敗しているのは、以下の処理がエラーとなるためです。
post password_resets_path, params: { password_reset: { email: "" } }
そもそもapp/controllers/password_resets_controller.rb
にcreate
メソッドが存在しないためですね。
create
メソッドの定義
class PasswordResetsController < ApplicationController
def new
end
+
+ def create
+ end
def edit
end
end
create
メソッドを定義したことにより、テストはどうなるか
# rails test test/integration/password_resets_test.rb
Running via Spring preloader in process 90
Started with run options --seed 62884
FAIL["test_password_resets", PasswordResetsTest, 3.0227256000002853]
test_password_resets#PasswordResetsTest (3.02s)
Expected true to be nil or false
test/integration/password_resets_test.rb:15:in `block in <class:PasswordResetsTest>'
1/1: [===================================] 100% Time: 00:00:02, Time: 00:00:02
Finished in 3.02778s
1 tests, 2 assertions, 1 failures, 0 errors, 0 skips
assert_not flash.empty?
「フラッシュメッセージが空であってはならないところ、フラッシュメッセージが空である」という理由でテストが失敗していますね。
フラッシュメッセージの定義
ひとまずはこのような感じの実装になります。
def create
+ if false #TODO: 有効なユーザー情報を与えられるようにする
+ #TODO: 有効なメールアドレスが与えられた場合の処理を実装する
+ else
+ flash.now[:danger] = "Email address not found"
+ end
end
フラッシュメッセージを定義したことにより、テストはどうなるか
# rails test test/integration/password_resets_test.rb
Running via Spring preloader in process 103
Started with run options --seed 35812
FAIL["test_password_resets", PasswordResetsTest, 3.379763599999933]
test_password_resets#PasswordResetsTest (3.38s)
expecting <"password_resets/new"> but rendering with <[]>
test/integration/password_resets_test.rb:16:in `block in <class:PasswordResetsTest>'
1/1: [===================================] 100% Time: 00:00:03, Time: 00:00:03
Finished in 3.38277s
1 tests, 3 assertions, 1 failures, 0 errors, 0 skips
assert_template 'password_resets/new'
「password_resets/new
がブラウザに返されるべきところ、何もブラウザに返されていない」という理由でテストが失敗していますね。
new
ビューをブラウザに返すようにする
このような感じの実装になります。
def create
if false #TODO: 有効なユーザー情報を与えられるようにする
#TODO: 有効なメールアドレスが与えられた場合の処理を実装する
else
flash.now[:danger] = "Email address not found"
+ render 'new'
end
end
new
ビューをブラウザに返すようにしたことにより、テストはどうなるか
# rails test test/integration/password_resets_test.rb
Running via Spring preloader in process 116
Started with run options --seed 5029
1/1: [===================================] 100% Time: 00:00:01, Time: 00:00:01
Finished in 1.94821s
1 tests, 3 assertions, 0 failures, 0 errors, 0 skips
ここまでのテストは成功するようになりました。これでひとまず「メールアドレスが無効な場合の処理」は完成といえるでしょう。
実は、最初に実装したテストはまだ不十分だった
この記事を書いた後に気がついたのですが、実は、Railsチュートリアル本文に書かれたテストでは、一つ検出できないバグがあります。すなわち、以下のようなコードを書いてしまった場合に発生するバグです。
def create
if false #TODO: 有効なユーザー情報を与えられるようにする
#TODO: 有効なメールアドレスが与えられた場合の処理を実装する
else
flash[:danger] = "Email address not found" # <-- flash.nowではなくflash
end
end
正しくは以下のコードになります。
def create
if false #TODO: 有効なユーザー情報を与えられるようにする
#TODO: 有効なメールアドレスが与えられた場合の処理を実装する
else
- flash[:danger] = "Email address not found"
+ flash.now[:danger] = "Email address not found"
end
end
詳細については、以下の別項目で解説しています。
-
Railsチュートリアル本文においては、「先に実装を書いて、後から一気にテストを書いていく」という進め方をしています。しかしながら、個人的には、「テストを先に実装して、後から実装で追いかけていく」ほうが理解しやすいと思ったのです。 ↩