TDDでテストが書けないことは、論文が書けないことと似ている : Railsチュートリアル備忘録 - 10章

# Abstract

## 目標:

`edit`、`update`、`index`、`destroy`アクションを実装し、UserモデルのRESTアクションを完成させる

## この章の気付き

### TDDはユーザーの意図（〜したい）と相性が良い

- 〜したいという意図をもとに考えるとテストがスムーズに書ける
- **演繹法的思考プロセス**
- 〜したいという引き出しの多さ、セオリー的な知識・経験の蓄積も重要かと
- 経験的に、限られたリソースで必要十分なアウトプットをするためにはこういった思考プロセスが重要

### 可読性という概念

- `unless`表記はときに可読性の向上に役立つ
- (実行内容) unless helper_method?(論理値を返すヘルパーメソッド)の表記が慣例
- **テストコードにおいては特に無理にDRYにせず、他者からのレビューを意識して書くべき**
- **可読性の高さ=シンプルなで的確なロジックであり、テストの目的とよく合致するかもしれない**

### セキュリティ対策

- Strong Parametersの設定は重要
- 編集可能なものだけ設定する、その他は許可しない、という振る舞いが重要
- リンクを隠すだけでなく、リクエストそのものを潰す必要性に気づいた

### セキュリティモデルの実装には`before_action`を活用する

- only: [...]で`before_action`の適応される範囲を制限可能
- before_actionで定義するアクションは`private`へ（**ここちょっと曖昧**）

### `edit`と`new`アクションの共通点と差異、それを吸収する`form with`メソッドの振る舞い

- `form with`は従来の`form tag`(URLを受ける)と`form for`(モデルを受け取る)の両者の特性を使い分ける
- モデルを受けることで、モデルの中身に応じた分岐ができる（中身なし > `create`, 中身あり > update）
- モデルは複数受け取る事ができるらしい

### `target="_blank"`のセキュリティ上の問題を埋める`rel="noopener"`

- 外部リンク`target="_blank"`と`rel="noopener"はセットで`

### モデルに属性を追加すると自然と`属性名?`メソッドが利用できるようになる

- adminの認可にはこれがそのまま使える

### DB上のデータを一覧表示するには繰り返し処理が適切

- インスタンス変数を定義`@user = User.all`して、@user.each do |user|で展開

### ページネーションの実装には`gem`を活用

- 複数ユーザーを作成する場合`gem faker`が便利

### ほか

- `redirect`はmethodの最終行や、returnが明示された後に実行される

- `create!`ユーザーが無効な場合に`false`を返すのではなく例外を発生させる > デバックの効率化

- Markdownエディター"Typora"最高

  [強力なMarkdownエディタ「Typora」に今更入門 - Qiita](https://qiita.com/4_mio_11/items/223326c3289f6b2c2a07)

- 引用URLの取得にsimple url copyというChrome extentionが便利

  [【Chrome Extension】簡単にURLとタイトルをコピーできる「simple-url-copy」作りました - フリーランチ食べたい](https://blog.ikedaosushi.com/entry/2019/02/25/012319)



## 関連した学び

### いい加減sessionってなんぞ？となったので調べてみた

[Sessionはブラウザを閉じると消去されるように設計されたCookieであり、それをやり取りするメソッドだった: Railsチュートリアル備忘録 - RailsのSessionとは? - Qiita](https://qiita.com/daddy_hacker/items/4d902310d468f08fce60)



## 必要要件

### ユーザを更新できる

- `get edit`に対応する`edit`アクションとビューファイル
  - `form with`部分はユーザー新規作成時のフォームを再利用可能
- `patch user_path(@user)`に対応する`update`アクション
- 正しい内容を入力した場合
  - flashメッセージが表示される
  - プロフィールページにリダイレクト
  - テータベースの内容が変更される
  - パスワードは空欄でも動作する
- 正しくない内容(validationに引っかかる)を入力した場合
  - 誤った内容で`patch`リクエストを送信
  - editページを`render`
  - エラーに応じたflashメッセージが表示される

### セキュリティモデルを適応する

- ログインした状態でのみ`edit`, `update`アクションが実行可能にする
  - ログインせずに`edit`, `update`アクションをリクエストした場合
  - エラーメッセージを表示し
  - ログインページにリダイレクトする
- アカウントの所有者のみが、情報を編集できるようにする
  - ログインしたユーザーと異なるユーザーに対する`edit`, `update`アクションがリクエストされた場合
  - エラーメッセージを表示し
  - `root_path`にリダイレクトする

- フレンドリーフォワーディング
  - アクセスしようとしたページにリダイレクトする

### ユーザー一覧を表示する

- このページはログインしていないと表示されない
- Userモデルの`index`アクション、`index.html.erb`の作成
- DB上のユーザが一覧で表示される（画像、名前（プロフィールリンク）の構造）

### ユーザーを削除できる

- ユーザーを削除できる管理者機能を付与する
- 管理者のみがユーザー一覧ページよりユーザーを削除できる
- 管理者自身を削除できないようにする

# 備忘録:第10章ユーザーの更新・表示・削除

## 10.1 ユーザーを更新する

### 10.1.1 編集フォーム

Viewでインスタンス変数を使えるようにする
URLの構造が/users/1/editでユーザーのidは`params[:id]`で取り出せる

```ruby
  def edit
    @user = User.find(params[:id])
  end
```

app/views/users/edit.html.erbを作成

```
$ touch app/views/users/edit.html.erb
```

edit.html.erbと重複が多いのでパーシャル化することで省略できるとのことだが

そもそも`new`と`edit`で求められる機能が異なるにも関わらず同じコード(`form with`)で記述できることに疑問が浮かぶ
Progateだとhtmlのフォームタグを直接記述して``<input ... value=<%=...  %> >``と記述していたような

ブラックボックス感が強く苦手意識ありましたが
この章で理解が深まりました

参考
[【Rails】form_with/form_forについて【入門】 - Qiita](https://qiita.com/snskOgata/items/44d32a06045e6a52d11c)

### 演習:

`target="_blank"`のセキュリティ上の問題を埋める`rel="noopener"`

```html
<a href="https://gravatar.com/emails" target="_blank" rel="noopener">change</a>
```

`form with`部分をパーシャル化する

```
$ touch app/views/users/_form.html.erb
```

ここにform with部分を挿入
`new.html.erb`と`edit.html.erb`でボタン部分のテキストのみ異なるので
各ページで<% provide (:button_text, '(text)') %>と与えて
パーシャルの中身では`yield(:button_text)`で呼び出す

この時点では`update`アクションがまだ定義されていない



### 10.1.2 編集の失敗

失敗に対応した`update`アクションの定義
`create`とほぼ同様の挙動、'user_params'メソッドで*Strong Parameters*を利用
`update`アクションを用いることが異なる

```
  def update
    @user = User.find(params[:id])
    if @user.update(user_params)
    
    else
      render 'edit'
    end
  end
```

### 10.1.3 編集失敗時のテスト

```
$ rails generate integration_test users_edit
```

validationに引っかかる内容で
`patch`リクエストを送る挙動を再現すればいい

```
test "unsuccessful edit" do
    get edit_user_path(@user)
    assert_template 'users/edit'
    patch user_path(@user), params: { user: { name:  "",
                                              email: "foo@invalid",
                                              password:              "foo",
                                              password_confirmation: "bar" } }

    assert_template 'users/edit'
  end
```

演習:

上記で4つのエラーが含まれておりそれをエラーメッセ時から検証したい
追加すべき内容は

```ruby
assert_select "div.alert", "The form contains 4 errors."
```



ちょっと応用してさっきのパーシャル化したformが正しく働いているかを確認するオリジナルテスト

```ruby
test "correct form for edit" do
  get edit_user_path(@user)
  assert_select 'input[name=commit][value="Save changes"]'
end
```



### 10.1.4 TDDで編集を成功させる

想定されるユーザーエクスペリエンスをもとに
テストを定義する
パスワードを変更しないためpasswordはblankになっている

```ruby
  test "successful edit" do
    get edit_user_path(@user)
    assert_template 'users/edit'
    name  = "Foo Bar"
    email = "foo@bar.com"
    patch user_path(@user), params: { user: { name:  name,
                                              email: email,
                                              password:              "",
                                              password_confirmation: "" } }
    assert_not flash.empty?
    assert_redirected_to @user
    @user.reload
    assert_equal name,  @user.name
    assert_equal email, @user.email
  end
```

現時点でtestは(RED)

@user.update(user_params)が成功した場合の
`edit`アクションを追加する

```ruby
flash[:success] = "Profile updated"
redirect_to @user
```

まだtestは(RED)

パスワードが空欄(`nil`)となっていることが`validation`にかかっている

`nil`をスルーさせるためにvalidationに`allow_nil: true`を加える

```ruby
  validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
```

新規登録時は`has_secure_password`による`validation`で空のパスワードを防ぐことができる

これでtest (GREEN)



## 10.2 認可

### 10.2.1 ユーザーにログインを要求する

セキュリティモデルを実装する
`before_action`を用いるのがよい

```ruby
before_action :logged_in_user, only: [:edit, :update]
```

`logged_in_user`を`private`で定義

```ruby
.
.
.  
private
	def logged_in_user
    unless logged_in?
      flash[:danger] = "Please log in"
      redirect_to login_url
    end
  end
end
```

unlessの挙動については
[【初心者必見】Rubyのunlessの使い方まとめ！ | 侍エンジニア塾ブログ（Samurai Blog） - プログラミング入門者向けサイト](https://www.sejuku.net/blog/15923)

ここで``rails test`とするとUsersEditTestに3 failures
テスト環境でログインできていないことが原因

すでに実装済みのテストヘルパー`log_in_as(user)`を活用するとよい
`test/integration/users_edit_test.rb`のテストに
それぞれ`log_in_as(@user)`を追加

再びテストで(GREEN)

**ただしこの状態ではbefore_actionをコメントアウトしてもテストは(GREEN)**
セキュリティモデルが機能していることを確認するために
`edit`, `update`それぞれのアクションが想定する動作をしているか検証する

```ruby
test "should..." go
	get edit_user_path(@user) # リクエスト(or patch)
	assert_not flash.empty? # エラーメッセージがある
	assert_redirected_to login_url # リダイレクト
end
```

これでbefore_actionのコメントアウトを外せば
`rails test`で(RED) > (GREEN)



### 10.2.2 正しいユーザーを要求する

アカウントの所有者のみがアカウントの情報を編集できるようにする

TDD開発ですすめる
テストに必要なべつのユーザーをつくる
`test/fixtures/users.yml`fixtureファイルに二人目を定義

setupにて`@other_user`を定義

```ruby
	def setup
    @user       = users(:michael)
    @other_user = users(:archer)
  end
```

テストの流れは
先程のテストに`log_in_as(@other_user)`が加わり、リダイレクト先が`root_url`へ

```ruby
 test "should..." do
    log_in_as(@other_user) # other_userでログイン
    get edit_user_path(@user) # @userのedit(もしくはupdate)アクションをリクエスト
    assert flash.empty? # エラーメッセージがある
    assert_redirected_to root_url #リダイレクト
  end
```

ここで追加した2つのテストがfailure

同様にbefore_actionでセキュリティモデルを実装していく

```ruby
before_action :correct_user,   only: [:edit, :update]
```

やはり同様に`private`に`correct_user`を定義する

```ruby
		def correct_user
      @user = User.find(params[:id])
      redirect_to(root_url) unless @user == current_user
    end
```

これで`rails test`(GREEN)

`@user == current_user`の部分を
論理値を返す`current_user?`をヘルパーメソッドとして定義することで書き直す

```ruby:app/helpers/sessions_helper.rb
	def current_user?(user)
    user && user == current_user
  end
```

これを使って実際にコードを書き直すと
可読性が増す

```ruby
		def correct_user
      @user = User.find(params[:id])
      redirect_to(root_url) unless current_user?(@user)
    end
```



### 10.2.3 フレンドリーフォワーディング

再びTDD
もはややりたいことをテストで書いたほうが理解しやすい

```ruby
test "successful edit with friendly forwarding" do
    get edit_user_path(@user) # editアクションをリクエスト（ログインしていない）
    log_in_as(@user) # ログインする
    assert_redirected_to edit_user_url(@user)　# アクセスしようとしていたページにリダイレクトさせたい
    .
    .
    .
end
```

これを
アクセスしようとしたURLを記憶する`store_location`と
実際にそのURLにリダイレクトする`redirect_back_or`の2つのヘルパーメソッドを使って実装

まず`store_location`
`request.original_url` はリクエストされたURLを返す
`if request.get?`で制限しないと生じる不具合について想像できなかったが
このようにすることが望ましいとのこと

```ruby
	def store_location
    session[:forwarding_url] = request.original_url if request.get?
  end
```

ここで以下のような疑問が
しかしこれはうまく回避される（後述）

```ruby
get edit #ログインしていないならloginにリダイレクト
get login #ここで`session[:forwarding_url]`が更新されないの?
```

これをbeforeアクション`logged_in_user`に追加

```ruby
		def logged_in_user
      unless logged_in?
        store_location 
        flash[:danger] = "Please log in."
        redirect_to login_url　
      end
    end
```

この`before_action`は`get login`リクエストに対して実行されないので
先程の疑問はうまく回避されることになる

つぎに`redirect_back_or(default)`を定義

```ruby
	def redirect_back_or(default)
    redirect_to(session[:forwarding_url] || default)
    session.delete(:forwarding_url)
  end
```

値が`nil`でなければ`session[:forwarding_url]` が
そうでなければ`default`へリダイレクト

`session.delete(:forwarding_url)`も重要
`forwarding_url`へのリダイレクトが繰り返さえることを防ぐ

`SessionsController`の`create`アクションに
`redirect_back_or`を追加

```ruby
	def create
    user = User.find_by(email: params[:session][:email].downcase)
    if user && user.authenticate(params[:session][:password])
      log_in user
      params[:session][:remember_me] == '1' ? remember(user) : forget(user)
      redirect_back_or user
    else
      flash.now[:danger] = 'Invalid email/password combination'
      render 'new'
    end
  end
```



## 10.3 すべてのユーザーを表示する

*すべての*ユーザーを一覧表示する`index`アクションを追加する

### 10.3.1 ユーザーの一覧ページ

ユーザー一覧のページはログインしていない状態では表示させないように___したい___
TDDですね

```ruby
  test "should redirect index when not logged in" do
    get users_path #ログインせずにリクエスト
    assert_redirected_to login_url # loginへリダイレクト
  end
```

`$ rails test` > (RED)

すでに実装済みの`before_action `が利用できる
beforeフィルターを書き換える

```ruby
before_action :logged_in_user, only: [:index, :edit, :update]
```

`index`アクションがないよと言われるので
Userモデルに定義

```ruby
def index
end
```

ここで`$ rails test` > (GREEN)

Viewファイルを作成していく

インスタンス変数を定義して

```ruby
def index
@users = User.all
end
```

ユーザーを一覧で表示するために繰り返し処理を用いる

```html

<% provide(:title, 'All users') %>
<h1>All users</h1>

<ul class="users">
  <% @users.each do |user| %>
    <li>
      <%= gravatar_for user, size: 50 %>
      <%= link_to user.name, user %>
    </li>
  <% end %>
</ul>
```

CSSに手を加えて

Headerのリンクを修正

`$ rails test` > (GREEN)

### 演習

> レイアウトにあるすべてのリンクに対して統合テストを書いてみましょう。ログイン済みユーザーとそうでないユーザーのそれぞれに対して、正しい振る舞いを考えてください。*ヒント*: `log_in_as`ヘルパーを使ってリスト 5.32にテストを追加してみましょう。

私はこう書きました

```ruby
require 'test_helper'

class SiteLayoutTest < ActionDispatch::IntegrationTest
  
  def setup
    @user = users(:michael)
  end
  
  test "layout-links when not login" do
    get root_path
    assert_template 'static_pages/home'
    assert_select "a[href=?]", root_path, count: 2
    assert_select "a[href=?]", help_path
    assert_select "a[href=?]", about_path
    assert_select "a[href=?]", contact_path
    get contact_path
    assert_select "title", full_title("Contact") 
    get signup_path
    assert_select "title", full_title("Sign up") 
  end
  
  test "layout-links when login" do
    log_in_as @user
    get root_path
    assert_template 'static_pages/home'
    assert_select "a[href=?]", root_path, count: 2
    assert_select "a[href=?]", help_path
    assert_select "a[href=?]", about_path
    assert_select "a[href=?]", contact_path
    assert_select "a[href=?]", users_path
    assert_select "a[href=?]", user_path(@user)
    assert_select "a[href=?]", edit_user_path(@user)
    assert_select "a[href=?]", logout_path
  end
  
end
```

`$ rails test` > (GREEN)



### 10.3.2 サンプルのユーザー

ユーザーを生成する`faker` gemを利用する
（本来開発環境以外では使用しないが、後で使うのですべての環境で利用できるようにする）

`Gemfile`の編集と`bundle install`したら

`db/seeds.rb`にユーザーを追加するコードを書く

```ruby
# メインのサンプルユーザーを1人作成する
User.create!(name:  "Example User",
             email: "example@railstutorial.org",
             password:              "foobar",
             password_confirmation: "foobar")

# 99人分の追加のユーザー生成する
99.times do |n|
  name  = Faker::Name.name
  email = "example-#{n+1}@railstutorial.org"
  password = "password"
  User.create!(name:  name,
               email: email,
               password:              password,
               password_confirmation: password)
end
```

> `create!`は基本的に`create`メソッドと同じものですが、ユーザーが無効な場合に`false`を返すのではなく例外を発生させる点が異なります。こうしておくと見過ごしやすいエラーを回避できるので、デバッグが容易になります。

```
$ rails db:migrate:reset
$ rails db:seed
```

データベースにユーザーを追加してくれる



### 10.3.3 ページネーション

ページネーション=ページで分割する

ページネーション用の`gem` `will_paginate`と
それにBootstrapのページネーションスタイルを適応する
`gem` `bootstrap-will_paginate`をGemfileに追加して
`bundle install`

ユーザーのリスト上下に
`<%= will_paginate %>`を置く（ガイド用のリンクになる）

そして`<% @users.each do |user| %>`の`@uses`に`paginate`された`User.all`を渡せば良い
`index`アクションの部分で

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

`params[:page]`の部分はビューに埋め込まれた`will_paginate`が生成して渡してくれる
こうすることでデフォルトで30サンプルずつにページネートし
`page:`でリクエストされたページのUserオブジェクトを返す（`page:2`なら31-60個目まで）

**ブラウザでプレビューするのに`rails server`の再起動が必要でした**

### 演習

>Railsコンソールを開き、pageオプションにnilをセットして実行すると、１ページ目のユーザーが取得できることを確認してみましょう。

途中で、あれ？はじめてindexビューが表示されるときは？と思ったのですが
`params[:page] = nil`で`params[:page] = 1`のときと同じ結果が返されます

>先ほどの演習課題で取得したpaginationオブジェクトは、何クラスでしょうか? また、User.allのクラスとどこが違うでしょうか? 比較してみてください。

```
>> User.paginate(page: 1).class                                                       
=> User::ActiveRecord_Relation
>> User.all.class
=> User::ActiveRecord_Relation
```

どちらも同じ`User::ActiveRecord_Relation`クラスです

ビューに埋め込まれたガイドがページをリクエストし
`paginate`メソッドがリクエストに応じたidのデータを抽出しリロードしている
というような挙動と想像されます

### 10.3.4 ユーザー一覧のテスト

以下を検証する

ログイン (`log_in_as`)
indexページにアクセス(`get` )
最初のページにユーザーがいることを確認 (`page: nil`の挙動を確認)
ページネーションのリンクがあることを確認 (`assert_select`)
(テスト用のデータベースに31人以上のユーザー必要)

テスト環境なので
`test/fixtures/users.yml`に31人分のfixturesを追加

```
<% 30.times do |n| %>
	user_<%= n %>:
  name:  <%= "User #{n}" %>
  email: <%= "user-#{n}@example.com" %>
  password_digest: <%= User.digest('password') %>
<% end %>
```

統合テストをつくる

```
$ rails generate integration_test users_index
```

```ruby
require 'test_helper'

class UsersIndexTest < ActionDispatch::IntegrationTest

  def setup
    @user = users(:michael)
  end

  test "index including pagination" do
    log_in_as(@user)
    get users_path
    assert_template 'users/index'
    assert_select 'div.pagination'
    User.paginate(page: 1).each do |user|
      assert_select 'a[href=?]', user_path(user), text: user.name
    end
  end
end
```

`each.do`のところが分かりづらかったですが

`paginate`されたUserオブジェクトに対応する
`assert_select 'a[href=?]', user_path(user), text: user.name`つまり
`<a href=#{user_path(user)}...>#{user.name}</a>`が存在するか確認していると思われる

`$ rails test` > (GREEN)



### 10.3.5 パーシャルのリファクタリング

`<%= render @users %>`
Userを列挙し`_user.html.erb`パーシャルで出力する
パーシャル部分にリストを仕込んでおけば良い

DRYであるがもはや変態



## 10.4 ユーザーを削除する

### 10.4.1 管理ユーザー

`User`モデルに新たな属性`admin`(boolean型)を追加する

```
$ rails generate migration add_admin_to_users admin:boolean
```

```
$ rails db:migrate
```

とすると`admin?`で論理値を返してくれるようになる

*Strong Parameters*を設定したことでこのadminは書き換えることができない
admin権限が付与されては困るのでセキュリティを考えると重要

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



### 演習

> Web経由でadmin属性を変更できないことを確認してみましょう。具体的には、リスト 10.56に示したように、PATCHを直接ユーザーのURL（/users/:id）に送信するテストを作成してみてください。テストが正しい振る舞いをしているかどうか確信を得るために、まずはadminをuser_paramsメソッド内の許可されたパラメータ一覧に追加するところから始めてみましょう。最初のテストの結果は red になるはずです。最後の行では、更新済みのユーザー情報をデータベースから読み込めることを確認します（ 6.1.5）。

以下のうように書きました

```ruby
 test "should not allow the admin attribute to be edited via the web" do
    log_in_as(@other_user)
    assert_not @other_user.admin?
    patch user_path(@other_user), params: {
                                    user: { password:              "password",
                                            password_confirmation: "password",
                                            admin: true } } # admin: trueをpatchしても
    assert_not @other_user.reload.admin? # reloadでDBからデータを取ってくるとadmin: false
  end
```

`$ rails test` > (GREEN)



### 10.4.2 destroyアクション

```ruby
	<% if current_user.admin? && !current_user?(user) %>
    | <%= link_to "delete", user, method: :delete,
                                  data: { confirm: "You sure?" } %>
  <% end %>
```

`current_user.admin? && !current_user?(user) `の部分ですが
管理者でログインしており、自分のプロフィールでない(`!`)場合に削除リンクを表示するという挙動です

`destroy`アクションを定義

```ruby
  def destroy
    User.find(params[:id]).destroy
    flash[:success] = "User deleted"
    redirect_to users_url
  end
```

リンクを隠しただけでなく
`destroy`アクションへのアクセスも制限されるべき
以下のような構成にする

```ruby
class UsersController < ApplicationController
  before_action :logged_in_user, only: [:index, :edit, :update, :destroy]　#beforeフィルター: ログインされているか
  before_action :correct_user,   only: [:edit, :update]
  before_action :admin_user,     only: :destroy #beforeフィルター: 管理者であるか
  .
  .
  .
  private
    .
    .
    .
    # 管理者かどうか確認
    def admin_user
      redirect_to(root_url) unless current_user.admin?
    end
end
```



### 10.4.3 ユーザー削除のテスト

fixtureの一つを`admin: true`に

まずは直前に定義したUsersControllerのアクション単位でテストを行って
その後統合テストへ

アクションのテストは`destroy`アクションが呼ばれたときに
ログインしていなければユーザーが削除されることなく`login_path`にリダイレクト
管理者でなければユーザーが削除されることなく`root_path`にリダイレクトを確認

まず前者
削除されていないことを`User.count`が変化しないと考える

```ruby
test "should redirect destroy when not logged in" do
  assert_no_difference `User.count` do
    delete user_path(@user)
  end
  assert_redirected_to login_url
end
```

assert_no_differenceはブロック内の処理を実施した前後で評価結果が変わらないことを主張する
[Minitestのassert_differenceメソッドについて - Qiita](https://qiita.com/toda-axiaworks/items/1b54997bff9ceb4e940b)

次に統合テスト
`/sample_app/test/integration/users_index_test.rb`を再利用
フローをまとめると

- adminユーザーでログイン
-  レイアウト、リンクの確認(実装済み)
- admin(自身)以外のプロフィールに`<a href=... >delete</a>`が含まれる
- `delete user_path(user)`でユーザーが削除されることを確認

削除されるユーザーをもうひとり定義する必要がある

フローを書き出してから自分でコードにしてみた

```ruby
require 'test_helper'

class UsersIndexTest < ActionDispatch::IntegrationTest
  
  def setup
    @admin = users(:michael)
    @non_admin = users(:archer)
  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', count: 2 #演習を反映
    User.paginate(page: 1).each do |user| #ローカル変数を使っていない
      assert_select 'a[href=?]', user_path(user), text: user.name
      unless user.admin? #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
  
  test "index as non-admin" do
    log_in_as(@non_admin)
    get users_path
    assert_select 'a', text: 'delete', count: 0
  end
  
end
```

多少違う箇所があるが意図はくめていると思う

unless部分をはじめ以下のように書いたが
一行が長くなってしまうので可読性の観点から無理にDRYにするのをやめた

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

[テストコードの期待値はDRYを捨ててベタ書きする ～テストコードの重要な役割とは？～ - Qiita](https://qiita.com/jnchito/items/eb3cfa9f7db752dcb796)