0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Railsチュートリアル 第12章 パスワードの再設定 - PasswordResets#createで、メールアドレスが無効な場合の処理をテスト駆動で実装していく

Last updated at Posted at 2019-12-12

何について書いているか

「Railsチュートリアル 第12章 パスワードの再設定」の「createアクションでパスワード再設定」のうち、メールアドレスが無効な場合の実装を、テスト駆動で行っていこうという内容です1

メールアドレスが無効な場合の処理に対するテストの実装

Railsチュートリアル本文の通りに実装していくとすれば、以下のテストが「メールアドレスが無効な場合の処理に対するテスト」に該当することになります。

test/integration/password_resets_test.rb
  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

現時点でテストが失敗しているのは、以下の処理がエラーとなるためです。

test/integration/password_resets_test.rb(14行目)
post password_resets_path, params: { password_reset: { email: "" } }

そもそもapp/controllers/password_resets_controller.rbcreateメソッドが存在しないためですね。

createメソッドの定義

PasswordResetsController
  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
test/integration/password_resets_test.rb(15行目)
assert_not flash.empty?

「フラッシュメッセージが空であってはならないところ、フラッシュメッセージが空である」という理由でテストが失敗していますね。

フラッシュメッセージの定義

ひとまずはこのような感じの実装になります。

PasswordResetsController#create
  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
test/integration/password_resets_test.rb(16行目)
assert_template 'password_resets/new'

password_resets/newがブラウザに返されるべきところ、何もブラウザに返されていない」という理由でテストが失敗していますね。

newビューをブラウザに返すようにする

このような感じの実装になります。

PasswordResetsController#create
  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チュートリアル本文に書かれたテストでは、一つ検出できないバグがあります。すなわち、以下のようなコードを書いてしまった場合に発生するバグです。

PasswordResetsController#create(バグあり)
def create
  if false #TODO: 有効なユーザー情報を与えられるようにする
    #TODO: 有効なメールアドレスが与えられた場合の処理を実装する
  else
    flash[:danger] = "Email address not found"  # <-- flash.nowではなくflash
  end
end

正しくは以下のコードになります。

PasswordResetsController#create
  def create
    if false #TODO: 有効なユーザー情報を与えられるようにする
      #TODO: 有効なメールアドレスが与えられた場合の処理を実装する
    else
-     flash[:danger] = "Email address not found"
+     flash.now[:danger] = "Email address not found"
    end
  end

詳細については、以下の別項目で解説しています。

  1. Railsチュートリアル本文においては、「先に実装を書いて、後から一気にテストを書いていく」という進め方をしています。しかしながら、個人的には、「テストを先に実装して、後から実装で追いかけていく」ほうが理解しやすいと思ったのです。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?