テストを実装するための前提
「ユーザーのプロフィール画面に、当該ユーザーのマイクロポストを表示する機能の実装」についてのテストは、実際にテストを実装していく前に必要となる準備がいくつかあります。
プロフィール画面用の統合テストを生成する
まずはプロフィール画面用の統合テストを生成することから始まります。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
という属性に、有効なユーザー名を渡します。
most_recent:
content: "Writing a short test"
created_at: <%= Time.zone.now %>
+ user: rhakurei
「有効なユーザー名」というのは、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」としています。
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
上記テストコードの重要なポイントは以下です。
-
ApplicationHelper
をinclude
することにより、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行目は、現時点の私の環境では以下のコードが記述されています。
assert_match @user.microposts.count.to_s, response.body
この内容ということは、「マイクロポストの投稿数が表示されていない」という旨のメッセージですね。
ユーザーのshow
ページに、「マイクロポストの投稿数表示」を実装する
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行目は、現時点の私の環境では以下のコードが記述されています。
assert_select 'div.pagination'
確かに「pagination
クラスを持つdiv
タグが必要である」ということですね。
ユーザーのshow
ページに、will_paginate
メソッドを追加する
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行目は、現時点の私の環境では以下のコードが記述されています。
@user.microposts.paginate(page: 1).each do |micropost|
assert_match micropost.content, response.body
end
「マイクロポストの内容がHTML中に見当たらない」という趣旨のメッセージのようです。
ユーザーのshow
ページに、「マイクロポストの投稿数表示」を実装する
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
テストスイート全体を対象としたテストも無事成功しましたね。