0
0

More than 3 years have passed since last update.

Railsチュートリアル 第13章 ユーザーのマイクロポスト - 「プロフィール画面へのマイクロポストの表示」を、テスト駆動で実装していく

Posted at

テストを実装するための前提

「ユーザーのプロフィール画面に、当該ユーザーのマイクロポストを表示する機能の実装」についてのテストは、実際にテストを実装していく前に必要となる準備がいくつかあります。

プロフィール画面用の統合テストを生成する

まずはプロフィール画面用の統合テストを生成することから始まります。rails generate integration_testコマンドですね。

# rails generate integration_test users_profile
Running via Spring preloader in process 1793
      invoke  test_unit
      create    test/integration/users_profile_test.rb

マイクロポストのfixtureをユーザーと関連付ける

各マイクロポストの:userという属性に、有効なユーザー名を渡します。

test/fixtures/microposts.yml
  most_recent:
    content: "Writing a short test"
    created_at: <%= Time.zone.now %>
+   user: rhakurei

「有効なユーザー名」というのは、test/fixtures/users.ymlに存在するユーザー名を意味します。

test/fixtures/users.yml
rhakurei:
  name: Reimu Hakurei
  email: rhakurei@example.com
  # ...略

多数のマイクロポストを生成する

マイクロポストに対するページネーション機能のテストを行うためには、ある程度まとまった数(今回であれば30数個以上)のマイクロポストを準備する必要があります。その内容をいちいち考えて書くのは無駄な労力といえます。

幸いにして、我々はFaker gemを用いてマイクロポストの本文内容を自動生成することが可能です。また、Railsの機能を使えば、fixture中で多数のマイクロポストを効率よく生成することも可能です。

Railsのfixtureを前提とした場合、多数のマイクロポストを生成するには、fixture中で埋め込みRubyを用いると便利です。

<% 30.times do |n| %>
micropost_<%= n %>:
  content: <%= Faker::Lorem.sentence(5) %>
  created_at: <%= 42.days.ago %>
  user: rhakurei
<% end %>

この例では、Faker::Lorem.sentenceメソッドによってランダムな文字列を生成し、その内容をマイクロポストの内容としています。

「ユーザーのプロフィール画面に、当該ユーザーのマイクロポストを表示する機能の実装」についてのテストの内容

上記準備を終えれば、テストそのものの実装に移ることができます。

test/integration/users_profile_test.rbの内容は以下のようになります。テストの名前は「profile display」としています。

test/integration/users_profile_test.rb
require 'test_helper'

class UsersProfileTest < ActionDispatch::IntegrationTest
  include ApplicationHelper

  def setup
    @user = users(:rhakurei)
  end

  test "profile display" do
    get user_path(@user)
    assert_template 'users/show'
    assert_select 'title', full_title(@user.name)
    assert_select 'h1', text: @user.name
    assert_select 'h1>img.gravatar'
    assert_match @user.microposts.count.to_s, response.body
    assert_select 'div.pagination'
    @user.microposts.paginate(page: 1).each do |micropost|
      assert_match micropost.content, response.body
    end
  end
end

上記テストコードの重要なポイントは以下です。

  • ApplicationHelperincludeすることにより、ApplicationHelper#full_titleメソッドが利用可能なようにしている
  • 生成されたHTMLにマイクロポストの数が出力されているかどうかの確認にresponse.bodyを用いている
    • response.bodyには、HTMLのbody要素のみならず、完全なHTMLが含まれている
  • assert_matchを用いれば、「HTMLのどこかに存在する」というようなテストが可能である
    • 「どの要素に含まれているか」は関係ない、というのがポイント
  • h1>img.gravatarというのは、「h1要素の内側にあるgravatarクラス付きのimg要素」を意味する
    • こうした入れ子関係に対してのテストも可能である

テスト駆動で実装を追加していく

テストが実装できたので、今度はテストに対応する実装を追加していきましょう。

「プロフィール画面のビューにマイクロポストの表示を実装する」前の段階でのテストの結果

「プロフィール画面のビューにマイクロポストの表示を実装する」前の段階で当該テストを実行した結果は以下になります。

# rails test test/integration/users_profile_test.rb
Running via Spring preloader in process 1825
Started with run options --seed 15695

 FAIL["test_profile_display", UsersProfileTest, 2.75337920000311]
 test_profile_display#UsersProfileTest (2.75s)
        Expected /34/ to match ...略.
        test/integration/users_profile_test.rb:16:in `block in <class:UsersProfileTest>'

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

Finished in 2.76194s
1 tests, 6 assertions, 1 failures, 0 errors, 0 skips

「...略」の部分には、response.bodyの内容全体が出力されます。

test/integration/users_profile_test.rbの16行目は、現時点の私の環境では以下のコードが記述されています。

test/integration/users_profile_test.rb(16行目)
assert_match @user.microposts.count.to_s, response.body

この内容ということは、「マイクロポストの投稿数が表示されていない」という旨のメッセージですね。

ユーザーのshowページに、「マイクロポストの投稿数表示」を実装する

app/views/users/show.html.erbの対応する実装は以下です。

app/views/users/show.html.erb
  <% provide(:title, @user.name) %>
  <div class="row">
    <aside class="col-md-4">
      <section class="user_info">
        <h1>
          <%= gravatar_for @user %>
          <%= @user.name %>
        </h1>
      </section>
    </aside>
+   <div class="col-md-8">
+     <% if @user.microposts.any? %>
+       <h3>Microposts (<%= @user.microposts.count %>)</h3>
+     <% end %>
+   </div>
  </div>

if @user.microposts.any?というのは、「ユーザーのマイクロポストが一つも存在しない場合、空のリストを表示させることはしない」という趣旨を表します。

「マイクロポストの投稿数表示」の実装を終えた時点でのテストの結果

# rails test test/integration/users_profile_test.rb
Running via Spring preloader in process 1838
Started with run options --seed 39528

 FAIL["test_profile_display", UsersProfileTest, 2.8101306000025943]
 test_profile_display#UsersProfileTest (2.81s)
        Expected at least 1 element matching "div.pagination", found 0..
        Expected 0 to be >= 1.
        test/integration/users_profile_test.rb:17:in `block in <class:UsersProfileTest>'

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

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

paginationクラスを持つdivタグがない」という趣旨のメッセージが表示されて、テストが失敗しています。

test/integration/users_profile_test.rbの17行目は、現時点の私の環境では以下のコードが記述されています。

test/integration/users_profile_test.rb(17行目)
assert_select 'div.pagination'

確かに「paginationクラスを持つdivタグが必要である」ということですね。

ユーザーのshowページに、will_paginateメソッドを追加する

app/views/users/show.html.erbの対応する実装は以下です。

app/views/users/show.html.erb
  <% provide(:title, @user.name) %>
  <div class="row">
    <aside class="col-md-4">
      <section class="user_info">
        <h1>
          <%= gravatar_for @user %>
          <%= @user.name %>
        </h1>
      </section>
    </aside>
    <div class="col-md-8">
      <% if @user.microposts.any? %>
        <h3>Microposts (<%= @user.microposts.count %>)</h3>
+       <%= will_paginate @microposts %>
      <% end %>
    </div>
  </div>

これでpaginationクラスを持つdivタグが描画されるようになります。

「ユーザーのshowページへのwill_paginateメソッドの追加」を終えた時点でのテストの結果

# rails test test/integration/users_profile_test.rb
Running via Spring preloader in process 1851
Started with run options --seed 39965

 FAIL["test_profile_display", UsersProfileTest, 2.5906765999970958]
 test_profile_display#UsersProfileTest (2.59s)
        Expected /Writing\ a\ short\ test/ to match "...略".
        test/integration/users_profile_test.rb:19:in `block (2 levels) in <class:UsersProfileTest>'
        test/integration/users_profile_test.rb:18:in `block in <class:UsersProfileTest>'

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

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

「...略」の部分には、response.bodyの内容全体が出力されます。

test/integration/users_profile_test.rbの18〜20行目は、現時点の私の環境では以下のコードが記述されています。

test/integration/users_profile_test.rb(18〜20行目)
@user.microposts.paginate(page: 1).each do |micropost|
  assert_match micropost.content, response.body
end

「マイクロポストの内容がHTML中に見当たらない」という趣旨のメッセージのようです。

ユーザーのshowページに、「マイクロポストの投稿数表示」を実装する

app/views/users/show.html.erbの対応する実装は以下です。

app/views/users/show.html.erb
  <% provide(:title, @user.name) %>
  <div class="row">
    <aside class="col-md-4">
      <section class="user_info">
        <h1>
          <%= gravatar_for @user %>
          <%= @user.name %>
        </h1>
      </section>
    </aside>
    <div class="col-md-8">
      <% if @user.microposts.any? %>
        <h3>Microposts (<%= @user.microposts.count %>)</h3>
+       <ol class="microposts">
+         <%= render @microposts %>
+       </ol>
        <%= will_paginate @microposts %>
      <% end %>
    </div>
  </div>

「マイクロポストの投稿数表示」の実装を終えた時点でのテストの結果

# rails test test/integration/users_profile_test.rb
Running via Spring preloader in process 1864
Started with run options --seed 47117

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

Finished in 3.79243s
1 tests, 67 assertions, 0 failures, 0 errors, 0 skips

この時点で、test/integration/users_profile_test.rbを対象としたテストが成功するようになりました。

テストスイート全体を対象としたテストも実施してみましょう。

# rails test
Running via Spring preloader in process 1877
Started with run options --seed 52362

  56/56: [=================================] 100% Time: 00:00:09, Time: 00:00:09

Finished in 9.37355s
56 tests, 302 assertions, 0 failures, 0 errors, 0 skips

テストスイート全体を対象としたテストも無事成功しましたね。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0