エンジニア志望のブログ

Railsチュートリアル第7章

はじめに
Ruby on Railsチュートリアル(第6版)のメモ、演習の解答例を記述した記事です。
解答は個人のものなので、誤りがあればご指摘ください。
開発環境  Ruby: 2.7.2 , Rails: 6.1.4

メモ

debugger

byebug gemのdebuggerメソッドをコード内に埋め込むことで埋め込んだ箇所の状況を調べることができる。
調べたあとはコード内からdebuggerを取り除く必要がある。

ヘルパー

form_with

form_withヘルパーはAcrive Recordのオブジェクトを取り込み、取り込んだオブジェクトの属性を使ってフォームを構築する。
参考:form_with | Railsドキュメント

content_tag

HTMLとERBが混ざっているときに置き換えるとコードが簡潔になる。
7.4.4の演習2を参照
参考:
content_tag | Railsドキュメント

ストロングパラメータ

マスアサインメントの脆弱性を対策するためのもの。
必須のパラメータと許可されたパラメータを指定して受け取ることができる。
こうすることで意図的にパラメータを書き換えられることを防ぐ

params.require(:user).permit(:name, :email, :password, :password_confirmation)

必須の属性を:userとし、名前、メールアドレス、パスワード、パスワードの確認を許可している。それ以外のパラメータはエラーとして弾くようになっている。

flash

一度だけメッセージなどを表示したいときに使う。
flashはハッシュのような使い方をする。

flash[:success] = "Welcome to the Sample App!"

参考:flash | Railsドキュメント

演習

7.3.4

演習1

リスト 7.20で実装したエラーメッセージに対するテストを書いてみてください。どのくらい細かくテストするかはお任せします。リスト 7.25にテンプレートを用意しておいたので、参考にしてください。

POSTリクエストを送信した際のエラーメッセージを追加

  test "invalid signup information" do
    get signup_path
    assert_no_difference "User.count" do
      post users_path, params: { user: { name: "", email: "user@invalid", password: "foo", password_confirmation: "bar" } }
    end
    assert_template 'users/new'
    assert_select 'div#error_explanation'
    assert_select 'div.field_with_errors'
    assert_select 'li', "Name can't be blank"
    assert_select 'li', "Email is invalid"
    assert_select 'li', "Password confirmation doesn't match Password"
    assert_select 'li', "Password is too short (minimum is 6 characters)"
  end

7.4.4

演習1

7.4.2で実装したflashに対するテストを書いてみてください。どのくらい細かくテストするかはお任せします。リスト 7.32に最小限のテンプレートを用意しておいたので、参考にしてください(FILL_INの部分を適切なコードに置き換えると完成します)。ちなみに、テキストに対するテストは壊れやすいです。文量の少ないflashのキーであっても、それは同じです。筆者の場合、flashが空でないかをテストするだけの場合が多いです。

  test "valid signup information" do
    get signup_path
    assert_difference "User.count", 1 do
      post users_path, params: { user: { name: "Example User", email: "user@example.com", password: "password", password_confirmation: "password" }}
    end
    follow_redirect!
    assert_template 'users/show'
    assert_not flash.empty?
  end
演習2

本文中でも指摘しましたが、flash用のHTML(リスト 7.29)は読みにくいです。より読みやすくしたリスト 7.33のコードに変更してみましょう。変更が終わったらテストスイートを実行し、正常に動作することを確認してください。なお、このコードでは、Railsのcontent_tagというヘルパーを使っています。

<div class="alert alert-<%= message_type %>"><%= message %></div>

HTMLとERBが混在しているコードをcontent_tagで置き換えると

<%= content_tag(:div, message, class: "alert alert-#{message_type}")%>
演習3

リスト 7.26のリダイレクトの行をコメントアウトすると、テストが失敗することを確認してみましょう。

# redirect_to user_url @user

テストの結果

yuy@yu sample_app % rails test
Running via Spring preloader in process 45196
Started with run options --seed 2921

ERROR["test_valid_signup_information", #<Minitest::Reporters::Suite:0x0000000114b9c728 @name="UsersSignupTest">, 0.32726700004423037]
 test_valid_signup_information#UsersSignupTest (0.33s)
RuntimeError:         RuntimeError: not a redirect! 204 No Content
            test/integration/users_signup_test.rb:24:in `block in <class:UsersSignupTest>'

  20/20: [=============================] 100% Time: 00:00:00, Time: 00:00:00

Finished in 0.42140s
20 tests, 47 assertions, 0 failures, 1 errors, 0 skips
演習4

リスト 7.26で、@user.saveの部分をfalseに置き換えたとしましょう(バグを埋め込んでしまったと仮定してください)。このとき、assert_differenceのテストではどのようにしてこのバグを検知するでしょうか? テストコードを追って考えてみてください

  def create
    @user = User.new(user_params)
    if false
      flash[:success] = "Welcome to the Sample App!"
      redirect_to user_url @user
    else
      render 'new'
    end
  end

POSTリクエストを送信したあとのデータベースの変化についてエラーが出ている。

yuy@yu sample_app % rails test     
Running via Spring preloader in process 46971
Started with run options --seed 47383

 FAIL["test_invalid_signup_information", #<Minitest::Reporters::Suite:0x000000011fc5b988 @name="UsersSignupTest">, 0.45664799999212846]
 test_invalid_signup_information#UsersSignupTest (0.46s)
        Expected at least 1 element matching "div#error_explanation", found 0..
        Expected 0 to be >= 1.
        test/integration/users_signup_test.rb:11:in `block in <class:UsersSignupTest>'

 FAIL["test_valid_signup_information", #<Minitest::Reporters::Suite:0x000000011fc50cb8 @name="UsersSignupTest">, 0.4575549999717623]
 test_valid_signup_information#UsersSignupTest (0.46s)
        "User.count" didn't change by 1.
        Expected: 1
          Actual: 0
        test/integration/users_signup_test.rb:21:in `block in <class:UsersSignupTest>'

  20/20: [=============================] 100% Time: 00:00:00, Time: 00:00:00

Finished in 0.51240s
20 tests, 42 assertions, 2 failures, 0 errors, 0 skips

おわりに

本章の終わりに本番環境の設定をしましたが、そこではセキュリティ面とユーザーが利用しやすい環境を作ることが大切だということが窺えました。

引き続き次の章もがんばります!!