Try T.M Engineer Blog

多摩市で生息するエンジニアが「アウトプットする事は大事だ」と思って始めたブログ

【Ruby on Railsチュートリアル(第4版)】第7章 ユーザー登録(演習と解答)

はじめに

このブログ記事は、私(Kodak)自身のRailsの勉強記録として書いています。
Ruby on Railsチュートリアル』の演習と解答をもくもくと書いているだけの記事なので、興味の無い方は軽くスルーしてあげてください。他にも、まだ『Railsチュートリアル』の演習に挑戦していない方、これから『Railsチュートリアル』をやるぞ!という方は(演習の解答の)ネタバレになりますので、スルーしてください。

Ruby on Rails チュートリアルとは?

Ruby業界でRailsを使い始めるなら、まず最初に初めるRails入門サイトです。
電子書籍版は有料ですが、Webサイトにあるオンライン版は無料なので、誰でも読む(Railsにチャレンジする)事ができます。
railstutorial.jp
全部で14章あり、かなりのボリュームですが、これを読む事でRailsの基礎を学ぶ事ができ、ちょっとしたWebアプリケーションを作れるレベルにはなれる?とのこと。
各章毎に演習問題が複数あり、これを解いていく事でRailsへの理解を深めていく事ができる様になっています。

環境について

Ruby 2.5.0-dev
Rails 5.1.4
・バージョン管理ツール:GitHub(https://github.com/Kodak4400)

演習と解答

7.1.1 デバックとRails環境<演習>
1. ブラウザから /about にアクセスし、デバッグ情報が表示されていることを確認してください。このページを表示するとき、どのコントローラとアクションが使われていたでしょうか? paramsの内容から確認してみましょう。
【解答】以下の通り。

 controller: static_pages
 action: about

2. Railsコンソールを開き、データベースから最初のユーザー情報を取得し、変数userに格納してください。その後、puts user.attributes.to_yamlを実行すると何が表示されますか? ここで表示された結果と、yメソッドを使ったy user.attributesの実行結果を比較してみましょう。
【解答】以下の通り。

$ rails console --sandbox
Loading development environment in sandbox (Rails 5.1.4)
Any modifications you make will be rolled back on exit
irb(main):001:0> user = User.first
  User Load (0.2ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2018-08-05 15:04:19", updated_at: "2018-08-05 15:04:19", password_digest: "$2a$10$RYgKGzk99xGmlvFo5SG6eeGYPmTug89rApFF0i.hq/v...">
irb(main):002:0> puts user.attributes.to_yaml
---
id: 1
name: Michael Hartl
email: mhartl@example.com
created_at: !ruby/object:ActiveSupport::TimeWithZone
  utc: &1 2018-08-05 15:04:19.200613000 Z
  zone: &2 !ruby/object:ActiveSupport::TimeZone
    name: Etc/UTC
  time: *1
updated_at: !ruby/object:ActiveSupport::TimeWithZone
  utc: &3 2018-08-05 15:04:19.200613000 Z
  zone: *2
  time: *3
password_digest: "$2a$10$RYgKGzk99xGmlvFo5SG6eeGYPmTug89rApFF0i.hq/vlxdh2d5ooG"
=> nil
irb(main):003:0> y user.attributes
---
id: 1
name: Michael Hartl
email: mhartl@example.com
created_at: !ruby/object:ActiveSupport::TimeWithZone
  utc: &1 2018-08-05 15:04:19.200613000 Z
  zone: &2 !ruby/object:ActiveSupport::TimeZone
    name: Etc/UTC
  time: *1
updated_at: !ruby/object:ActiveSupport::TimeWithZone
  utc: &3 2018-08-05 15:04:19.200613000 Z
  zone: *2
  time: *3
password_digest: "$2a$10$RYgKGzk99xGmlvFo5SG6eeGYPmTug89rApFF0i.hq/vlxdh2d5ooG"
=> nil
irb(main):004:0>

7.1.2 Usersリソース<演習>
1. 埋め込みRubyを使って、マジックカラム (created_atとupdated_at) の値をshowページに表示してみましょう (リスト 7.4)。
【解答】以下の通り。

### app/views/users/show.html.erb
<%= @user.name %>, <%= @user.email %>, <%= @user.created_at %>, <%= @user.updated_at %>

2. 埋め込みRubyを使って、Time.nowの結果をshowページに表示してみましょう。ページを更新すると、その結果はどう変わっていますか? 確認してみてください。
【解答】以下の通り。

### app/views/users/show.html.erb
<%= Time.now => |
<%= @user.name %>, <%= @user.email %>, <%= @user.created_at %>, <%= @user.updated_at %>

7.1.3 debuggerメソッド<演習>
1. showアクションの中にdebuggerを差し込み (リスト 7.6)、ブラウザから /users/1 にアクセスしてみましょう。その後コンソールに移り、putsメソッドを使ってparamsハッシュの中身をYAML形式で表示してみましょう。ヒント: 7.1.1.1の演習を参考にしてください。その演習ではdebugメソッドで表示したデバッグ情報を、どのようにしてYAML形式で表示していたでしょうか?
【解答】以下の通り。

(byebug) puts @user.attributes.to_yaml
---
id: 1
name: Example User
email: example@railstutorial.org
created_at: !ruby/object:ActiveSupport::TimeWithZone
  utc: &1 2018-08-05 15:04:19.200613000 Z
  zone: &2 !ruby/object:ActiveSupport::TimeZone
    name: Etc/UTC
  time: *1
updated_at: !ruby/object:ActiveSupport::TimeWithZone
  utc: &3 2018-08-06 15:37:45.025375000 Z
  zone: *2
  time: *3
password_digest: "$2a$10$72.Qvt0xhg4Ceay1EGQ.weaTRki26bZanGeBqxNRSxim1KGbqGBNu"
nil
(byebug)

2. newアクションの中にdebuggerを差し込み、/users/new にアクセスしてみましょう。@userの内容はどのようになっているでしょうか? 確認してみてください。
【解答】以下の通り。

(byebug) @user
nil
(byebug)

7.1.4 Gravatar画像とサイドバー<演習>
1. (任意) Gravatar上にアカウントを作成し、あなたのメールアドレスと適当な画像を紐付けてみてください。メールアドレスをMD5ハッシュ化して、紐付けた画像がちゃんと表示されるかどうか試してみましょう。
実機操作のため割愛。

2. 7.1.4で定義したgravatar_forヘルパーをリスト 7.12のように変更して、sizeをオプション引数として受け取れるようにしてみましょう。うまく変更できると、gravatar_for user, size: 50といった呼び出し方ができるようになります。重要: この改善したヘルパーは10.3.1で実際に使います。忘れずに実装しておきましょう。
【解答】以下の通り。

### app/helpers/users_helper.rb
module UsersHelper

  # 引数で与えられたユーザーのGravatar画像を返す
  def gravatar_for(user, options = { size: 80 })
    gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
    size = options[:size]
    gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}"
    image_tag(gravatar_url, alt: user.name, class: "gravatar")
  end
end
### app/views/users/show.html.erb
<% provide(:title, @user.name) %>
<div class="row">
  <aside class="col-md-4">
    <section class="user_info">
      <h1>
        <%= gravatar_for @user, size: 50 %>
        <%= @user.name %>
      </h1>
    </section>
  </aside>
</div>

3. オプション引数は今でもRubyコミュニティで一般的に使われていますが、Ruby 2.0から導入された新機能「キーワード引数 (Keyword Arguments)」でも実現することができます。先ほど変更したリスト 7.12を、リスト 7.13のように置き換えてもうまく動くことを確認してみましょう。この2つの実装方法はどういった違いがあるのでしょうか? 考えてみてください。
【解答】受け取り側でハッシュ値を取り出し、再設定する必要がない。

### app/helpers/users_helper.rb
module UsersHelper

  # 引数で与えられたユーザーのGravatar画像を返す
  def gravatar_for(user, size: 80 )
    gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
    gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}"
    image_tag(gravatar_url, alt: user.name, class: "gravatar")
  end
end

7.2.1 form_forを利用する<演習>
1. 試しに、リスト 7.15にある:nameを:nomeに置き換えてみましょう。どんなエラーメッセージが表示されるようになりますか?
【解答】以下の通り。

ActionView::Template::Error (undefined method `nome' for #<User:0x00007f90fe06b828>
Did you mean?  name):
     5:   <div class="col-md-6 col-md-offset-3">
     6:     <%= form_for(@user) do |f| %>
     7:       <%= f.label :nome %>
     8:       <%= f.text_field :nome %>
     9:
    10:       <%= f.label :email %>
    11:       <%= f.email_field :email %>

app/views/users/new.html.erb:8:in `block in _app_views_users_new_html_erb___3366810708409065086_70130356933080'
app/views/users/new.html.erb:6:in `_app_views_users_new_html_erb___3366810708409065086_70130356933080'

2. 試しに、ブロックの変数fをすべてfoobarに置き換えてみて、結果が変わらないことを確認してみてください。確かに結果は変わりませんが、変数名をfoobarとするのはあまり良い変更ではなさそうですね。その理由について考えてみてください。
【解答】form_forで使用する変数名として、fであれば変数名から「一時的に使用するブロック変数であること」「form_forの接頭辞を使用していること」が連想でる。しかし、foobarだと変数名からこれらが連想できないため、あまり良い変更ではない。

### app/views/users/new.html.erb
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_for(@user) do |foobar| %>
      <%= foobar.label :name %>
      <%= foobar.text_field :name %>

      <%= foobar.label :email %>
      <%= foobar.email_field :email %>

      <%= foobar.label :password %>
      <%= foobar.password_field :password %>

      <%= foobar.label :password_confirmation, "Confirmation" %>
      <%= foobar.password_field :password_confirmation %>

      <%= foobar.submit "Create my account", class:"btn btn-primary" %>
    <% end %>
  </div>
</div>

7.2.2 フォームHTML<演習>
1. Learn Enough HTML to Be DangerousではHTMLをすべて手動で書き起こしていますが、なぜformタグを使わなかったのでしょうか? 理由を考えてみてください。
【解答】入力フォーム作成時に、formタグを使用する。Learn Enough HTMLには、入力フォームが無いため、HTMLをすべて手動で書き起こしている。

7.3.2 Strong Parameters<演習>
1. /signup?admin=1 にアクセスし、paramsの中にadmin属性が含まれていることをデバッグ情報から確認してみましょう。
【解答】以下の通り。

parameters: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
  admin: '1'
  controller: users
  action: new

7.3.3 エラーメッセージ<演習>
1. 最小文字数を5に変更すると、エラーメッセージも自動的に更新されることを確かめてみましょう。
【解答】以下の通り。

### app/models/user.rb
class User < ApplicationRecord
  before_save { email.downcase! }
  validates :name, presence: true, length:{ maximum: 50 }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
  validates :email, presence: true, length:{ maximum: 255 },
    format:{ with: VALID_EMAIL_REGEX },
    uniqueness: { case_sensitive:false }
  has_secure_password
  validates :password, presence: true, length:{ minimum: 5 }
end

f:id:special-moucom:20180813104317p:plain

2. 未送信のユーザー登録フォーム (図 7.12) のURLと、送信済みのユーザー登録フォーム (図 7.18) のURLを比べてみましょう。なぜURLは違っているのでしょうか? 考えてみてください。
【解答】signupパスからコントローラアクションusers#createが呼ばれて、usersパスに遷移したため。
未送信のユーザー登録フォームのURL: http://localhost:3000/signup
送信済みのユーザー登録フォームのURL: http://localhost:3000/users

7.3.4 失敗時のテスト<演習>
1. リスト 7.20で実装したエラーメッセージに対するテストを書いてみてください。どのくらい細かくテストするかはお任せします。リスト 7.25にテンプレートを用意しておいたので、参考にしてください。
【解答】以下の通り。

### test/integration/users_signup_test.rb
require 'test_helper'

class UsersSignupTest < ActionDispatch::IntegrationTest

  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.alert-danger'
  end
end

2. 未送信のユーザー登録フォームと送信直後のURLは、それぞれ /signup と /users になり、URLが異なっています。これは、リスト 5.43で追加した名前付きルートと、デフォルトのRESTfulなルーティング (リスト 7.3) を設定したことによって生じた差異です。リスト 7.26とリスト 7.27の内容を追加し、この問題を解決してみてください。うまくいけば、いずれのURLも /signup となるはずです。あれ、でもテストは greenのままになっていますね...、なぜでしょうか? (考えてみてください)
【解答】以下の通り。

### config/routes.rb
Rails.application.routes.draw do
  root 'static_pages#home'
  get '/help', to:'static_pages#help'
  get '/about', to:'static_pages#about'
  get '/contact', to:'static_pages#contact'
  get '/signup', to:'users#new'
  post '/signup', to:'users#create'
  resources :users
end
### app/views/users/new.html.erb
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_for(@user, url:signup_path) do |f| %>
      <%= render 'shared/error_messages' %>

      <%= f.label :name %>
      <%= f.text_field :name, class:'form-control' %>

      <%= f.label :email %>
      <%= f.email_field :email, class:'form-control' %>

      <%= f.label :password %>
      <%= f.password_field :password, class:'form-control' %>

      <%= f.label :password_confirmation, "Confirmation" %>
      <%= f.password_field :password_confirmation, class:'form-control' %>

      <%= f.submit "Create my account", class:"btn btn-primary" %>
    <% end %>
  </div>
</div>
### signupパスにコントローラアクション:users#createが追加された。
### 加えて、formの呼び出し先のurlをsignupの上記コントローラアクションに変更しているため、urlがsignupから変わらなくなったもの。
$ rails routes
   Prefix Verb   URI Pattern               Controller#Action
     root GET    /                         static_pages#home
     help GET    /help(.:format)           static_pages#help
    about GET    /about(.:format)          static_pages#about
  contact GET    /contact(.:format)        static_pages#contact
   signup GET    /signup(.:format)         users#new
          POST   /signup(.:format)         users#create
    users GET    /users(.:format)          users#index
          POST   /users(.:format)          users#create
 new_user GET    /users/new(.:format)      users#new
edit_user GET    /users/:id/edit(.:format) users#edit
     user GET    /users/:id(.:format)      users#show
          PATCH  /users/:id(.:format)      users#update
          PUT    /users/:id(.:format)      users#update
          DELETE /users/:id(.:format)      users#destroy
$

3. リスト 7.25のpost部分を変更して、先ほどの演習課題で作った新しいURL (/signup) に合わせてみましょう。また、テストが greenのままになっている点も確認してください。
【解答】以下の通り。

### test/integration/users_signup_test.rb
require 'test_helper'

class UsersSignupTest < ActionDispatch::IntegrationTest

  test "invalid signup information" do
    get signup_path
    assert_no_difference 'User.count' do
      post signup_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.alert-danger'
  end
end
$ rails test
Finished in 0.570018s, 33.3323 runs/s, 71.9276 assertions/s.
19 runs, 41 assertions, 0 failures, 0 errors, 0 skips

4. リスト 7.27のフォームを以前の状態 (リスト 7.20) に戻してみて、テストがやはり greenになっていることを確認してください。これは問題です! なぜなら、現在postが送信されているURLは正しくないのですから。assert_selectを使ったテストをリスト 7.25に追加し、このバグを検知できるようにしてみましょう (テストを追加して redになれば成功です)。その後、変更後のフォーム (リスト 7.27) に戻してみて、テストが green になることを確認してみましょう。ヒント: フォームから送信してテストするのではなく、’form[action="/signup"]’という部分が存在するかどうかに着目してテストしてみましょう。
【解答】以下の通り。

### app/views/users/new.html.erb
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_for(@user) do |f| %>
      <%= render 'shared/error_messages' %>

      <%= f.label :name %>
      <%= f.text_field :name, class:'form-control' %>

      <%= f.label :email %>
      <%= f.email_field :email, class:'form-control' %>

      <%= f.label :password %>
      <%= f.password_field :password, class:'form-control' %>

      <%= f.label :password_confirmation, "Confirmation" %>
      <%= f.password_field :password_confirmation, class:'form-control' %>

      <%= f.submit "Create my account", class:"btn btn-primary" %>
    <% end %>
  </div>
</div>
$ rails test
Finished in 0.598132s, 31.7656 runs/s, 68.5467 assertions/s.
19 runs, 41 assertions, 0 failures, 0 errors, 0 skips
### test/integration/users_signup_test.rb
require 'test_helper'

class UsersSignupTest < ActionDispatch::IntegrationTest

  test "invalid signup information" do
    get signup_path
    assert_no_difference 'User.count' do
      post signup_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.alert-danger'
    assert_select "form[action=?]", signup_path
  end
end
$ rails test
Failure:
Expected at least 1 element matching "form[action="/signup"]", found 0..
Expected 0 to be >= 1.
### app/views/users/new.html.erb
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_for(@user, url:signup_path) do |f| %>
      <%= render 'shared/error_messages' %>

      <%= f.label :name %>
      <%= f.text_field :name, class:'form-control' %>

      <%= f.label :email %>
      <%= f.email_field :email, class:'form-control' %>

      <%= f.label :password %>
      <%= f.password_field :password, class:'form-control' %>

      <%= f.label :password_confirmation, "Confirmation" %>
      <%= f.password_field :password_confirmation, class:'form-control' %>

      <%= f.submit "Create my account", class:"btn btn-primary" %>
    <% end %>
  </div>
</div>
$ rails test
Finished in 0.780902s, 24.3308 runs/s, 53.7840 assertions/s.
19 runs, 42 assertions, 0 failures, 0 errors, 0 skips

7.4.1 登録フォーム完成<演習>
1. 有効な情報を送信し、ユーザーが実際に作成されたことを、Railsコンソールを使って確認してみましょう。
実機操作のため割愛。

2. リスト 7.28を更新し、redirect_to user_url(@user)とredirect_to @userが同じ結果になることを確認してみましょう。
【解答】以下の通り。

### app/controllers/users_controller.rb
class UsersController < ApplicationController

  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to @user
    else
      render 'new'
    end
  end

  private

    def user_params
     params.require(:user).permit(:name, :email, :password,
                                  :password_confirmation)
    end
end
### app/controllers/users_controller.rb
class UsersController < ApplicationController

  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to user_url(@user)
    else
      render 'new'
    end
  end

  private

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

7.4.2 flash<演習>
1. コンソールに移り、文字列内の式展開 (4.2.2) でシンボルを呼び出してみましょう。例えば"#{:success}"といったコードを実行すると、どんな値が返ってきますか? 確認してみてください。
【解答】以下の通り。

irb(main):004:0> "#{:success}"
=> "success"
irb(main):005:0>

2. 先ほどの演習で試した結果を参考に、リスト 7.30のflashはどのような結果になるか考えてみてください。
【解答】classは、"alert alert-success"となり、ハッシュ値のmessageが表示される。

7.4.3 実際のユーザー登録<演習>
1. Railsコンソールを使って、新しいユーザーが本当に作成されたのかもう一度チェックしてみましょう。結果は、リスト 7.32のようになるはずです。
【解答】以下の通り。

$ rails console
Loading development environment (Rails 5.1.4)
irb(main):001:0> User.find_by(email: "example@railstutorial.org")
  User Load (0.4ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "example@railstutorial.org"], ["LIMIT", 1]]
=> #<User id: 1, name: "Rails Tutorial", email: "example@railstutorial.org", created_at: "2018-08-12 15:23:47", updated_at: "2018-08-12 15:23:47", password_digest: "$2a$10$j8YTAFtYA/b8uo0q0gqNX.9Zox3VCDNwhYkjMd6ET87...">
irb(main):002:0>

2. 自分のメールアドレスでユーザー登録を試してみましょう。既にGravatarに登録している場合、適切な画像が表示されているか確認してみてください。
実機操作のため割愛。

7.4.3 実際のユーザー登録<演習>
1. 7.4.2で実装したflashに対するテストを書いてみてください。どのくらい細かくテストするかはお任せします。リスト 7.34に最小限のテンプレートを用意しておいたので、参考にしてください (FILL_INの部分を適切なコードに置き換えると完成します)。ちなみに、テキストに対するテストは壊れやすいです。文量の少ないflashのキーであっても、それは同じです。筆者の場合、flashが空でないかをテストするだけの場合が多いです。
【解答】以下の通り。

class UsersSignupTest < ActionDispatch::IntegrationTest

  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.nil?
  end
end

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

<!-- app/views/layouts/application.html.erb -->
<!DOCTYPE html>
<html>
  <head>
    <title><%= full_title(yield(:title)) %></title>
    <%= render 'layouts/rails_default' %>
    <%= render 'layouts/shim' %>
</head>
<body>
  <%= render 'layouts/header' %>
  <div class="container">
   <% flash.each do |message_type, message| %>
     <%= content_tag(:div, message, class:"alert alert-#{message_type}") %>
   <% end %>
   <%= yield %>
   <%= render 'layouts/footer' %>
   <%= debug(params) if Rails.env.development? %>
  </div>
</body>
</html>
$ rails test
Finished in 0.638803s, 31.3086 runs/s, 70.4443 assertions/s.
20 runs, 45 assertions, 0 failures, 0 errors, 0 skips

3. リスト 7.28のリダイレクトの行をコメントアウトすると、テストが失敗することを確認してみましょう。
【解答】以下の通り。

require 'test_helper'

class UsersSignupTest < ActionDispatch::IntegrationTest

  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.nil?
  end
end
$ rails test
Failure:
expecting <"users/show"> but rendering with <[]>

4. リスト 7.28で、@user.saveの部分をfalseに置き換えたとしましょう (バグを埋め込んでしまったと仮定してください)。このとき、assert_differenceのテストではどのようにしてこのバグを検知するでしょうか? テストコードを追って考えてみてください。
【解答】DBへの保存が失敗するため、assert_difference 'User.count'でエラーとなる。

$ rails test
Failure:
Expected at least 1 element matching "div#error_explanation", found 0..
Expected 0 to be >= 1.

7.5.3 本番環境へのデプロイ<演習>
1. ブラウザから本番環境 (Heroku) にアクセスし、SSLの鍵マークがかかっているか、URLがhttpsになっているかどうかを確認してみましょう。
実機操作のため割愛。

2. 本番環境でユーザーを作成してみましょう。Gravatarの画像は正しく表示されているでしょうか?
実機操作のため割愛。