【RailsTutorial】11章 解答（11.3.3−3のみ）

#前提
- Railsチュートリアル5.1
- [11.3.3](https://railstutorial.jp/chapters/account_activation?version=5.1#sec-activation_test_and_refactoring) 演習2及び3

11章の中では一番重い問題だと思われるがネット上にあまり解答を見かけなかったので自分で書いてみる。

他の問題は以下などが参考になると思われる。
[Ruby on Rails チュートリアル 5.0（第4版） 第11章 演習と解答まとめ](https://qiita.com/mochikichi321/items/745f482b282d6ad7e5fd)

なお筆者はRuby歴3ヶ月、Raisチュートリアルを初めて1.5ヶ月なので、ミスなどありましたらご指摘お願いします。

#演習とその解答

##11.3.3の演習2
###問題
> 
現在は、/usersのユーザーindexページを開くとすべてのユーザーが表示され、/users/:idのようにIDを指定すると個別のユーザーを表示できます。しかし考えてみれば、有効でないユーザーは表示する意味がありません。そこで、リスト 11.40のテンプレートを使って、この動作を変更してみましょう。なお、ここで使っているActive Recordのwhereメソッドについては、13.3.3でもう少し詳しく説明します。


リスト 11.40: 有効なユーザーだけを表示するコードのテンプレート

```app/controllers/users_controller.rb
class UsersController < ApplicationController
  .
  .
  .
  def index
    @users = User.where(activated: FILL_IN).paginate(page: params[:page])
  end

  def show
    @user = User.find(params[:id])
    redirect_to root_url and return unless FILL_IN
  end
  .
  .
  .
end
```

###解答
1つめのFILL_INはwhereでアクティブ化されたユーザだけを抽出するので、trueを入れれば良い。
2つめのFILL_INは後置unlessを使っているので、「アクティブ化されているユーザ以外はroot_urlにリダイレクトして終了（＝個別のユーザページにはアクセスさせない）」を実現すれば良い。
したがって、ユーザがアクティブ化されているかを調べる@User.activated?を入れる。

```app/controllers/users_controller.rb
class UsersController < ApplicationController
  .
  .
  .
  def index
    @users = User.where(activated: true).paginate(page: params[:page])
  end

  def show
    @user = User.find(params[:id])
    redirect_to root_url and return unless @User.activated?
  end
  .
  .
  .
end
```

##11.3.3の演習3
###問題
> 
ここまでの演習課題で変更したコードをテストするために、/users と /users/:id の両方に対する統合テストを作成してみましょう。

###解答

/usersはユーザ一覧、/users/:idはユーザの個別のプロフィールページを指す。
前問で、

- ユーザ一覧にアクティブ化されていないユーザを表示しない
- アクティブ化していないユーザがプロフィールページにアクセスしようとしたらルートにリダイレクトする

という変更を行った。

これをテストするためにはまず有効化されていないテスト用のユーザを作る必要がある。
そこでusers.ymlに以下のようなアクティブ化されていないユーザを追加する。
（※このときユーザ一覧の1ページ1目に表示されるように最初の方に追加する必要があることに注意)

```fixtures/users.yml
hoge:
  name: Hoge
  email: hoge@example.gov
  password_digest: <%= User.digest('password') %>
  activated: false
  activated_at: nil
```

この状態でテストを実行するとREDとなる。
エラーメッセージを見るとindexページの表示を確認するテストで引っかかっているようだ。
このときのテストのfailuresをまず以下のように解消する

```test/integration/user_index_test.rb
.
.
.
  test "index as admin including pagination and delete links" do
    log_in_as(@admin)
    get users_path
    assert_template 'users/index'
    assert_select 'div.pagination'
    # 下記のUserにwhereメソッドを付加
    first_page_of_users = User.where(activated: true).paginate(page: 1)
    first_page_of_users.each do |user|
      assert_select 'a[href=?]', user_path(user), text: user.name
      unless user == @admin
        assert_select 'a[href=?]', user_path(user), text: 'delete'
      end
    end
.
.
.
```

これでページ一覧の中でアクティブ化されているユーザのみをテストするようになるためテストをパスするようになる。

しかしこれではまだ本当にアクティブ化されていないユーザが表示されていないかは分からない。
そこで、下記のようにsetupメソッドでnon_activated_userを追加し、表示されていないことを確認するテストを書く。

```test/integration/users_index_test.rb
.
.
.
  def setup
    @admin = users(:michael)
    @non_admin = users(:archer)
    @non_activated_user = users(:hoge)
  end

  test "index as admin including pagination and delete links" do
    log_in_as(@admin)
    get users_path
    assert_template 'users/index'
    assert_select 'div.pagination'
    # 下記のUserにwhereメソッドを付加
    first_page_of_users = User.where(activated: true).paginate(page: 1)
    first_page_of_users.each do |user|
      assert_select 'a[href=?]', user_path(user), text: user.name
      unless user == @admin
        assert_select 'a[href=?]', user_path(user), text: 'delete'
      end
    end

    # 有効化されていないアカウントのリンクが無いことを確認
    assert_select 'a[href=?]', user_path(@non_activated_user), count: 0

    assert_difference 'User.count', -1 do
      delete user_path(@non_admin)
    end
  end
.
.
.
```

最後に

- アクティブ化されていないユーザでログイン
- ルートにリダイレクトされる

という動きをテストする。

```test/integration/users_login_test.rb
.
.
.
  def setup
    @user = users(:michael)
    # アクティブ化されていないユーザをセットアップ
    @non_activated_user = users(:hoge)
  end
.
.
.
  test "login as non-activated user" do
    log_in_as(@non_activated_user)
    assert_redirected_to root_url
  end
.
.
.
```

ここまでテストをパスすればおそらくこの演習はクリアとなるはず。

#最後に
本投稿が筆者の初めての投稿であった。
まとめる作業は意外と時間がかかるが理解度も大きく深まるということを身を持って知った。

この問題だけをピンポイントで探す人がいるかどうかは不明であるが、誰かの役に立つことを願う。