どこにも答えがない。Rails 5.1 第４版 11.3.3 演習３でつまずく

自分と同じように、Rails 5.1 第４版 11.3.3 演習３でつまずいた人のお役に立てるといいなと思いまとめました。

答えをまとめているサイトを３つくらい見たのですが、演習３だけは、答えを書いていないか、回答どおりにすると、別の箇所でエラーが起きます。（自分が探しきれていないだけかもしれません）

なので、自分でエラーを修正しました。

演習１は影響ないので飛ばします。

演習２

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

これに対して、リスト11.40はこのようになっています。

>リスト 11.40: 有効なユーザーだけを表示するコードのテンプレート
app/controllers/users_controller.rb


 ```ruby
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
```

この演習は、indexアクションにwhereメソッドを追加したことにより、activated: trueのユーザーが表示されるようになります。
また、showアクションについてはactivated?メソッドによって、アクティベート済みのユーザでなければ、root_urlにリダイレクトします。

なので、FILL_INの箇所が、以下のようになります。

```ruby
class UsersController < ApplicationController
  before_action :logged_in_user, only: [:index, :edit, :update, :destroy]
  before_action :correct_user,   only: [:edit, :update]
  before_action :admin_user,     only: :destroy
  
  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
```

この変更をしたことにより、アクティベートされていないユーザーは、indexページにひょうじされなくなりました。

次は演習３です

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

とあるので演習２に対するテストを作成します。

有効化されていないユーザーを作成しないことには、indexアクション、showアクションで表示されていないかを調べられないので、fixtures/users.ymlのユーザーをひとり、有効化されていないことにします。

fixtures/users.yml

```yml
.
.
.
lana:
  name: Lana Kane
  email: hands@example.gov
  password_digest: <%= User.digest('password') %>
  activated: false
  activated_at: <%= Time.zone.now %>
```

activated: falseにしました。

このユーザーをsetupメソッドに追加します。

```ruby
require 'test_helper'

class UsersIndexTest < ActionDispatch::IntegrationTest

  def setup
    @admin         = users(:michael)
    @non_admin     = users(:archer)
    @non_activated = users(:lana)
  end
```

あとは、テストを追加します。

app/controllers/users_controller.rb

```ruby
  test "not allow the not activated user" do
#アクティベートされていないユーザーを許可しない
    log_in_as(@non_activated)
#アクティベートしていないユーザーでログイン
    assert_not @non_activated.activated?
#アクティベートされていなければ、GREEN
    get users_path
#getリクエストを送る（ユーザー一覧ページ）
    assert_select "a[href=?]", user_path(@non_activated), count: 0
#ユーザー一覧ページのなかに、アクティベートされていないユーザのリンクがなければGREEN
    get user_path(@non_activated)
#getリクエストを送る（アクティベートされていないユーザーのprofileページを表示）
    assert_redirected_to root_url
#root_urlへリダイレクトしたらテストはGREEN
  end
```

これで、このテスト(test "not allow the not activated user" do end)はGREENなのですが、

test/integration/users_index_test.rb

```ruby
  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'
    first_page_of_users = User.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_difference 'User.count', -1 do
      delete user_path(@non_admin)
    end
  end
```

もとからあった、このテストがREDになります。
テストの結果このあたりの行でエラーがあると表示されたので、テストないようの、この部分を見ていきます。

```ruby
    first_page_of_users = User.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
```

first_page_of_user.each do |user|
でuser変数には、1ページ目のユーザーが代入されます。
そのユーザーの数だけ、
assert_select 'a[href=?]', user_path(user), text: user.name
でuser_pathをチェックするのですが、演習２をおこなったことにより、アクティベートされたユーザーが(activated: true)表示されています。なので、アクティベートされていないユーザーは表示されていません。

演習２の下記の箇所です。

```ruby
  def index
    @users = User.where(activated: true).paginate(page: params[:page])
  end
```

なので、アクティベートされていないユーザーのuser_pathがユーザー一覧のページに(users_path)表示されていないので、REDになります。

これを解決するために、アクティベートされていないユーザーは、このテストを回避することにします。それはそれで問題がおこりそうですが、

すでに、演習３で追加したテスト
test "not allow the not activated user"
の中で

```ruby
    assert_select "a[href=?]", user_path(@non_activated), count: 0
```

このように、アクティベートされていないユーザーのリンク(user_path)は表示されていない(count: 0)ことがわかっているので大丈夫だと判断します。

ということで、テスト内容を変更します。

```ruby
    first_page_of_users = User.paginate(page: 1)
    first_page_of_users.each do |user|
      unless user == @non_activated
        assert_select 'a[href=?]', user_path(user), text: user.name
      end
      unless user == @admin
        assert_select 'a[href=?]', user_path(user), text: 'delete'
      end
    end
```

追加した内容は、以下です。

```ruby
unless == @non_activated

end
```

この内容を追加したことによりアクティベートされていないユーザーは、

```ruby
assert_select 'a[href=?]', user_path(user), text: user.name
```

このテストを回避しました。

これでも、まだえらーがでるので、次は

```ruby
unless user == @admin
        assert_select 'a[href=?]', user_path(user), text: 'delete'
      end
```

この部分です。この時点で
admin属性がtrueのユーザーは、ユーザーを削除するリンクが表示されません。また、削除用のリンクである、deleteというテキストも表示されません。
なので、admin属性がtrueのユーザーは

```ruby
assert_select 'a[href=?]', user_path(user), text: 'delete'
```

このテストを通過しないようにしています。
これと同様に、アクティベートされていないユーザーも削除用のリンクが表示されませんし、deleteというテキストも表示されません。（そもそも、アクティベートされていないユーザーは、ユーザー一覧に表示されていません）
なので、アクティベートされていないユーザーもこのテストを通過しないようにします。

```ruby
unless user == @admin || @non_activated
        assert_select 'a[href=?]', user_path(user), text: 'delete'
      end
```

このように、|| @non_activatedとすることにより、
@adminか、@non_activatedはこのテストを通過しないことになりました。
ここでテストはGREENになります。

ためしに、演習２の内容を元にもどして、テストがREDになることを確認しました。

```
Expected response to be a <3XX: redirect>, but was a <200: OK>
```

テスト結果はリダイレクトのレスポンスがくるはずが、リダイレクトでなく、成功を示しているのでテストはREDです。

もう一度演習２の内容を直して。テストをすると、テストはGREENになります。

この演習内容だけは、自分で探した範囲では、詳細が書いていなかったので、まとめてみました。