Railsチュートリアル 第13章 ユーザーのマイクロポスト - ユーザーのフィード画面に対するテストは正しく動いているか

# 概要

「[Railsチュートリアル 第13章 ユーザーのマイクロポスト - マイクロポストを操作する - 演習「フィード画面のマイクロポストをテストする](https://railstutorial.jp/chapters/user_microposts?version=5.1#sec-exercises_micropost_tests)」より、演習1番目の内容です。Railsチュートリアル本文には以下のようにあります。

> [リスト 13.55](https://railstutorial.jp/chapters/user_microposts?version=5.1#code-microposts_interface_test)で示した４つのコメント (「無効な送信」など) のそれぞれに対して、テストが正しく動いているか確認してみましょう。具体的には、対応するアプリケーション側のコードをコメントアウトし、テストが`red`になることを確認し、元に戻すと`green`になることを確認してみましょう。

なお、「それぞれの確認で変更したソースコードは、確認が終わったら変更前の状態に戻すこと」を前提とします。

# 無効なマイクロポストを受け付けてしまう場合

無効なマイクロポストを受け付けてしまう例としては、「Micropostモデルのバリデーションが正常に機能していない場合」が想定できます。具体的には、「Micropostモデルの`content`属性に対するバリデーションが動作していない場合」です。当該バリデーションに関するソースコードを`app/models/micropost.rb`からコメントアウトしてみます。

```diff:app/models/micropost.rb
  class Micropost < ApplicationRecord
    belongs_to :user
    default_scope -> { order(created_at: :desc) }
    validates :user_id, presence: true
-   validates :content, presence: true, length: { maximum: 140 }
+   # validates :content, presence: true, length: { maximum: 140 }
  end
```

この状態で、`test/integration/microposts_interface_test.rb`を対象にテストを実行してみましょう。

```
# rails test test/integration/microposts_interface_test.rb
Running via Spring preloader in process 513
Started with run options --seed 28697

 FAIL["test_micropost_interface", MicropostsInterfaceTest, 4.152912300000025]
 test_micropost_interface#MicropostsInterfaceTest (4.15s)
        "Micropost.count" didn't change by 0.
        Expected: 38
          Actual: 39
        test/integration/microposts_interface_test.rb:18:in `block in <class:MicropostsInterfaceTest>'

  1/1: [===================================] 100% Time: 00:00:04, Time: 00:00:04

Finished in 4.15470s
1 tests, 7 assertions, 1 failures, 0 errors, 0 skips
```

```
"Micropost.count" didn't change by 0.
Expected: 38
  Actual: 39
```

上記は「（当該テストテストコードの時点で変化してはならない）`Micropost.count`の値が変化してしまっている」という趣旨のメッセージですね。

また、私の環境では、`test/integration/microposts_interface_test.rb`の17行目から20行目のソースコードは以下のようになっています。

```ruby:test/integration/microposts_interface_test.rb（17〜20行目）
# 無効な送信
assert_no_difference 'Micropost.count' do
  post microposts_path, params: { micropost: { content: "" } }
end
```

想定通りの形でテストが失敗したということですね。

# 有効なマイクロポストが受け付けられない場合

逆に「無効なマイクロポストは受け付けられないが、有効なマイクロポストが受け付けられない」という場合です。

例えば「Strong Parameters機能の利用に際し、受け入れる属性の名前を間違えている」という場合は、このケースに該当するはずです。というわけで、`app/controllers/microposts_controller.rb`を以下のように変更した上で保存します。

```diff:app/controllers/microposts_controller.rb（抜粋）
  class MicropostsController < ApplicationController
    before_action :logged_in_user, only: [:create, :destroy]
    ...略
    private

      def micropost_params
-       params.require(:micropost).permit(:content)
+       params.require(:micropost).permit(:id)
      end
```

この状態で、`test/integration/microposts_interface_test.rb`を対象にテストを実行してみましょう。

```
# rails test test/integration/microposts_interface_test.rb
Running via Spring preloader in process 526
Started with run options --seed 19620

 FAIL["test_micropost_interface", MicropostsInterfaceTest, 3.3941545999987284]
 test_micropost_interface#MicropostsInterfaceTest (3.39s)
        "Micropost.count" didn't change by 1.
        Expected: 39
          Actual: 38
        test/integration/microposts_interface_test.rb:24:in `block in <class:MicropostsInterfaceTest>'

  1/1: [===================================] 100% Time: 00:00:03, Time: 00:00:03

Finished in 3.40014s
1 tests, 9 assertions, 1 failures, 0 errors, 0 skips
```

```
"Micropost.count" didn't change by 1.
Expected: 39
  Actual: 38
```

上記は「（当該テストテストコードの時点で1増えていなければならない）`Micropost.count`の値が1増えていない」という趣旨のメッセージですね。

また、私の環境では、`test/integration/microposts_interface_test.rb`の22行目から26行目のソースコードは以下のようになっています。

```ruby:test/integration/microposts_interface_test.rb（22〜26行目）
# 有効な送信
content = "This micropost really ties the room together"
assert_difference 'Micropost.count', 1 do
  post microposts_path, params: { micropost: { content: content } }
end
```

想定通りの形でテストが失敗したということですね。

# 投稿が削除されない場合

「投稿が削除されない」という事態が発生する原因はいくつか考えられます。

## マイクロポストをRDBから削除する処理の実装が抜けた場合

マイクロポストをRDBから削除する処理の実装が抜けた場合について確認してみましょう。一番単純なケースですね。

```diff:app/controllers/microposts_controller.rb
  class MicropostsController < ApplicationController
    before_action :logged_in_user, only: [:create, :destroy]
    before_action :correct_user, only: [:destroy]

    ...略

    def destroy
-     @micropost.destroy
+     # @micropost.destroy
      flash[:success] = "Micropost deleted"
      redirect_back(fallback_location: root_url)
    end

    private

      ...略
  end
```

上記変更を適用した状態で、`test/integration/microposts_interface_test.rb`に対してテストを実行してみましょう。

```
# rails test test/integration/microposts_interface_test.rb
Running via Spring preloader in process 552
Started with run options --seed 40696

 FAIL["test_micropost_interface", MicropostsInterfaceTest, 3.7064526000031037]
 test_micropost_interface#MicropostsInterfaceTest (3.71s)
        "Micropost.count" didn't change by -1.
        Expected: 38
          Actual: 39
        test/integration/microposts_interface_test.rb:33:in `block in <class:MicropostsInterfaceTest>'

  1/1: [===================================] 100% Time: 00:00:03, Time: 00:00:03

Finished in 3.71107s
1 tests, 14 assertions, 1 failures, 0 errors, 0 skips
```

```
"Micropost.count" didn't change by -1.
Expected: 38
  Actual: 39
```

上記は「（当該テストテストコードの時点で1減っていなければならない）`Micropost.count`の値が1減っていない」という趣旨のメッセージですね。

また、私の環境では、`test/integration/microposts_interface_test.rb`の30行目から35行目のソースコードは以下のようになっています。

```ruby:test/integration/microposts_interface_test.rb（30〜35行目）
# 投稿を削除する
assert_select 'a', text: 'delete'
first_micropost = @user.microposts.paginate(page: 1).first
assert_difference 'Micropost.count', -1 do
  delete micropost_path(first_micropost)
end
```

想定通りの形でテストが失敗したということですね。

## Beforeフィルターの実装内容に誤りがあった場合

今回実装したMicropostsコントローラーでは、マイクロポストの削除に際し、`logged_in_user`と`correct_user`という2つのBeforeフィルターが適用されます。その実装内容に誤りがあった場合も、マイクロポストの削除はうまくいかないはずです。

今回は、`correct_user`フィルターの実装内容を、意図的に誤ったものにしてみます。

```diff:app/controllers/microposts_controller.rb
  class MicropostsController < ApplicationController
    before_action :logged_in_user, only: [:create, :destroy]
    before_action :correct_user, only: [:destroy]

    ...略

    def destroy
      @micropost.destroy
      flash[:success] = "Micropost deleted"
      redirect_back(fallback_location: root_url)
    end

    private

      ...略

      def correct_user
        @micropost = current_user.microposts.find_by(id: params[:id])
-       redirect_to root_url if @micropost.nil?
+       redirect_to root_url unless @micropost.nil?
      end
  end
```

上記変更を適用した状態で、`test/integration/microposts_interface_test.rb`に対してテストを実行してみましょう。

```
# rails test test/integration/microposts_interface_test.rb
Running via Spring preloader in process 578
Started with run options --seed 23555

 FAIL["test_micropost_interface", MicropostsInterfaceTest, 4.179720099997212]
 test_micropost_interface#MicropostsInterfaceTest (4.18s)
        "Micropost.count" didn't change by -1.
        Expected: 38
          Actual: 39
        test/integration/microposts_interface_test.rb:33:in `block in <class:MicropostsInterfaceTest>'

  1/1: [===================================] 100% Time: 00:00:04, Time: 00:00:04

Finished in 4.20035s
1 tests, 14 assertions, 1 failures, 0 errors, 0 skips
```

前述「マイクロポストをRDBから削除する処理が抜けた場合」と同様の形でテストが失敗しています。

# 自身のページにマイクロポストの削除リンクが表示されない場合

```diff:app/views/microposts/_micropost.html.erb
  <li id="micropost-<%= micropost.id %>">
    <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
    <span class="user"><%= link_to micropost.user.name, micropost.user %></span>
    <span class="content"><%= micropost.content %></span>
    <span class="timestamp">
      Posted <%= time_ago_in_words(micropost.created_at) %> ago.
-     <% if current_user?(micropost.user) %>
+     <%# <% if current_user?(micropost.user) %> %>
-       <%= link_to "delete", micropost, method: :delete, data: { confirm: "You sure?" } %>
+       <%# <%= link_to "delete", micropost, method: :delete, data: { confirm: "You sure?" } %> %>
-     <% end %>
+     <%# <% end %> %>
    </span>
  </li>
```

上記変更を適用した状態で、`test/integration/microposts_interface_test.rb`に対してテストを実行してみましょう。

```
# rails test test/integration/microposts_interface_test.rb
Running via Spring preloader in process 617
Started with run options --seed 35207

 FAIL["test_micropost_interface", MicropostsInterfaceTest, 3.800804300000891]
 test_micropost_interface#MicropostsInterfaceTest (3.80s)
        <delete> expected but was
        <sample app>..
        Expected 0 to be >= 1.
        test/integration/microposts_interface_test.rb:31:in `block in <class:MicropostsInterfaceTest>'

  1/1: [===================================] 100% Time: 00:00:03, Time: 00:00:03

Finished in 3.81553s
1 tests, 13 assertions, 1 failures, 0 errors, 0 skips
```

```
<delete> expected but was
<sample app>..
Expected 0 to be >= 1.
```

「deleteというリンクテキストの`a要素`が、1つ以上存在するはずなのに1つも存在しない」という趣旨のメッセージですね。

また、私の環境では、`test/integration/microposts_interface_test.rb`の30行目から31行目のソースコードは以下のようになっています。

```ruby:test/integration/microposts_interface_test.rb（30〜31行目）
# 投稿を削除する
assert_select 'a', text: 'delete'
```

想定通りの形でテストが失敗したということですね。

# 違うユーザーのページにマイクロポストの削除リンクが表示される場合

```diff:app/views/microposts/_micropost.html.erb
  <li id="micropost-<%= micropost.id %>">
    <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
    <span class="user"><%= link_to micropost.user.name, micropost.user %></span>
    <span class="content"><%= micropost.content %></span>
    <span class="timestamp">
      Posted <%= time_ago_in_words(micropost.created_at) %> ago.
-     <% if current_user?(micropost.user) %>
+     <%# <% if current_user?(micropost.user) %> %>
+       <%= link_to "delete", micropost, method: :delete, data: { confirm: "You sure?" } %>
-     <% end %>
+     <%# <% end %> %>
    </span>
  </li>
```

上記変更を適用した状態で、`test/integration/microposts_interface_test.rb`に対してテストを実行してみましょう。

```
# rails test test/integration/microposts_interface_test.rb
Running via Spring preloader in process 604
Started with run options --seed 24198

 FAIL["test_micropost_interface", MicropostsInterfaceTest, 3.710591699997167]
 test_micropost_interface#MicropostsInterfaceTest (3.71s)
        Expected exactly 0 elements matching "a", found 2..
        Expected: 0
          Actual: 2
        test/integration/microposts_interface_test.rb:38:in `block in <class:MicropostsInterfaceTest>'

  1/1: [===================================] 100% Time: 00:00:03, Time: 00:00:03

Finished in 3.72132s
1 tests, 15 assertions, 1 failures, 0 errors, 0 skips
```

```
Expected exactly 0 elements matching "a", found 2..
Expected: 0
  Actual: 2
```

「1つも存在しないはずの（deleteというリンクテキストの）`a`要素が2つ存在する」というメッセージが表示されています。

また、私の環境では、`test/integration/microposts_interface_test.rb`の36行目から38行目のソースコードは以下のようになっています。

```ruby:test/integration/microposts_interface_test.rb（36〜38行目）
# 違うユーザーのプロフィールにアクセス（削除リンクがないことの確認）
get user_path(users(:mkirisame))
assert_select 'a', text: 'delete', count: 0
```

想定通りの形でテストが失敗したということですね。