Railsチュートリアル 第13章 ユーザーのマイクロポスト - 【発展】ユーザー情報のパーシャルとマイクロポスト投稿フォームを、テスト駆動開発で実装する

# 内容

Railsチュートリアル第13章の本文中において、テストが書かれることなしに実装されていた以下2つの要素を、テスト駆動で実装してみようというものです。

* ユーザー情報のパーシャル
* マイクロポスト投稿フォーム

Railsチュートリアル本文においては、「マイクロポスト作成フォームのあるホーム画面のモックアップ」として、[図 13.10](https://railstutorial.jp/chapters/user_microposts?version=5.1#fig-home_page_with_micropost_form_mockup)が挙げられています。

# ユーザー情報のパーシャルの実装

## ユーザー情報のパーシャルに含まれているべき要素に対するテスト

Railsチュートリアル本文によれば、サイドバーで表示されるべきユーザー情報のパーシャルには、以下の要素が含まれている必要があります。

* `gravatar_for`クラスを持つ`img`要素
* ユーザー名を内容とする`h1`要素
* マイクロポスト数を含む`span`要素

テストを追加するのは`test/integration/microposts_interface_test.rb`です。追加するテストの内容は以下となります。

```diff:test/integration/microposts_interface_test.rb
  require 'test_helper'

  class MicropostsInterfaceTest < ActionDispatch::IntegrationTest
    def setup
      @user = users(:rhakurei)
    end

    test "micropost interface" do
      log_in_as(@user)
      get root_path
+     assert_select 'img.gravatar'
+     assert_select 'h1', @user.name
+     assert_select 'span', /#{@user.microposts.count}/
      assert_select 'div.pagination'
      ...略
    end
  end
```

### 「指定の文字列が含まれる指定の要素が存在するか」というテスト

例えば「`@user.microposts.count`変数の内容が記述されている`span`要素が存在するか」というテストであれば、以下のように記載します。

```ruby:
assert_select 'span', /#{@user.microposts.count}/
```

正規表現オブジェクトを意味する`//`で、変数の内容を展開する`#{}`を囲っているのがポイントですね。

## サイドバーで表示されるべきユーザー情報のパーシャルに対しての、現時点のテスト結果

現時点での`test/integration/microposts_interface_test.rb`に対するテストの結果は、以下のようになります。

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

 FAIL["test_micropost_interface", MicropostsInterfaceTest, 3.629997299999559]
 test_micropost_interface#MicropostsInterfaceTest (3.63s)
        Expected at least 1 element matching "h1", found 0..
        Expected 0 to be >= 1.
        test/integration/microposts_interface_test.rb:12:in `block in <class:MicropostsInterfaceTest>'

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

Finished in 3.63972s
1 tests, 2 assertions, 1 failures, 0 errors, 0 skips
```

「`h1`要素が見当たらない」というメッセージを出してテストが失敗しています。

## サイドバーで表示するユーザー情報のパーシャルを実装する

まず、サイドバーで表示するユーザー情報のパーシャルの実体を実装していきます。ファイル名は`app/views/shared/_user_info.html.erb`とします。

```erb:app/views/shared/_user_info.html.erb
<%= link_to gravatar_for(current_user, size: 50), current_user %>
<h1><%= current_user %></h1>
<span><%= link_to "view my profile", current_user %><span>
<span><%= pluralize(current_user.microposts.count, "micropost") %></span>
```

続いて、Homeページのビューでも当該パーシャルを使用するようにします。変更するファイルは`app/views/static_pages/home.html.erb`ですね。

```diff:app/views/static_pages/home.html.erb
  <% provide(:title, "Home") %>
  <% if logged_in? %>
    <div class="row">
      <aside class="col-md-4">
-       <%#TODO: ユーザー情報表示部の実装 %>
+       <section class="user_info">
+         <%= render 'shared/user_info' %>
+       </section>
        <section class="micropost_form">
          <%= render 'shared/micropost_form' %>
        </section>
      </aside>
      <div class="col-md-8">
        <h3>Micropost Feed</h3>
        <%= render 'shared/feed' %>
      </div>
    </div>
  <% else %>
    ...略
  <% end %>
```

# サイドバーで表示するユーザー情報のパーシャルを実装した時点でのテストの結果

ここまでの実装が完了した時点で、`test/integration/microposts_interface_test.rb`を対象としてテストを実行すると、結果は以下のようになります。

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

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

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

テストが成功していますね。ひとまず、サイドバーで表示するユーザー情報のパーシャルは正しく追加できたようです。

# マイクロポスト投稿フォームの完全な実装

## 完全なマイクロポストの投稿フォームに含まれているべき要素に対するテスト

Railsチュートリアル本文によれば、サイドバーで表示されるべきマイクロポスト投稿フォームのパーシャルには、以下の要素が含まれている必要があります。

* /microposts に対して`POST`アクションを行うフォーム
* マイクロポスト本文を入力するためのテキストフィールド

```diff:test/integration/microposts_interface_test.rb
  require 'test_helper'

  class MicropostsInterfaceTest < ActionDispatch::IntegrationTest
    def setup
      @user = users(:rhakurei)
    end

    test "micropost interface" do
      log_in_as(@user)
      get root_path
      assert_select 'img.gravatar'
      assert_select 'h1', @user.name
      assert_select 'span', /#{@user.microposts.count}/
+     assert_select 'form[action="/microposts"]'
+     assert_select 'textarea'
      assert_select 'div.pagination'
      ...略
    end
  end
```

## マイクロポスト投稿フォームに対するテストを実装した時点でのテストの結果

上記マイクロポスト投稿フォームに対するテストを実装した時点で、`microposts_interface_test.rb`に対してテストを実行してみましょう。

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

 FAIL["test_micropost_interface", MicropostsInterfaceTest, 3.1817765000014333]
 test_micropost_interface#MicropostsInterfaceTest (3.18s)
        Expected at least 1 element matching "textarea", found 0..
        Expected 0 to be >= 1.
        test/integration/microposts_interface_test.rb:15:in `block in <class:MicropostsInterfaceTest>'

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

Finished in 3.19284s
1 tests, 5 assertions, 1 failures, 0 errors, 0 skips
```

「`textarea`要素が存在しない」というメッセージを出力してテストが失敗しています。

## マイクロポスト投稿フォームの完全な実装

Railsチュートリアル本文によれば、マイクロポスト投稿フォームの完全な実装は以下の通りになります。

```erb:
<%= form_for(@micropost) do |f| %>
  <%= render 'shared/error_messages', object: f.object %>
  <div class="field">
    <%= f.text_area :content, placeholder: "Compose new micropost..." %>
  </div>
  <%= f.submit "Post", class: "btn btn-primary" %>
<% end %>
```

対応するソースファイルは`test/integration/microposts_interface_test.rb`です。

ここまでの実装で、エラーメッセージのパーシャルが出力できるようにするための実装は完了しています。新たに追加する必要があるのは以下の2つですね。

* マイクロポスト本文を入力するためのテキストフィールド
* 送信ボタン

早速実装を追加していきましょう。

```diff:app/views/shared/_micropost_form.html.erb
  <%= form_for(@micropost) do |f| %>
    <%= render 'shared/error_messages', object: f.object %>
+   <div class="field">
+     <%= f.text_area :content, placeholder: "Compose new micropost..." %>
+   </div>
+   <%= f.submit "Post", class: "btn btn-primary" %>
  <% end %>
```

## マイクロポスト投稿フォームの完全な実装を終えた時点でのテストの結果

`test/integration/microposts_interface_test.rb`に対し、改めてテストを実行してみましょう。

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

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

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

今度こそテストが成功しました。

テストスイート全体に対するテストの結果はどうでしょうか。

```
# rails test
Running via Spring preloader in process 354
Started with run options --seed 14679

  59/59: [=================================] 100% Time: 00:00:11, Time: 00:00:11

Finished in 11.81368s
59 tests, 321 assertions, 0 failures, 0 errors, 0 skips
```

テストスイート全体に対しても、無事テストが成功しましたね。