following
アクションおよびfollowers
アクションの統合テストにおける、Railsチュートリアル本文記載のテストの不具合
実は、Railsチュートリアル本文のリスト 14.29に記述されているテストには、1つの不具合があります。
例えば、app/views/users/show_follow.html.erb
に以下の欠落がある場合を考えてみましょう。
<% provide(:title, @title) %>
<div class="row">
<aside class="col-md-4">
<section class="user_info">
...略
</section>
<section class="stats">
<%= render'shared/stats' %>
<% if @users.any? %>
<div class="user_avatars">
<% @users.each do |user|%>
<%= link_to gravatar_for(user, size: 30), user %>
<% end %>
</div>
<% end %>
</section>
</aside>
<div class="col-md-8">
<h3><%= @title %></h3>
<% if @users.any? %>
<ul class="users follow">
- <%= render @users %>
</ul>
<%= will_paginate %>
<% end %>
</div>
</div>
の場合、例えば users/1/following のWebブラウザにおける表示は以下のようになります。
ページ右側にフォローしているユーザー一覧が描画されていません。明らかに意図した表示内容ではないですね。
しかしながら、Railsチュートリアル本文のリスト 14.29に記述されているテストの場合、この状態でもテストは成功してしまうのです。
# rails test test/integration/following_test.rb
Running via Spring preloader in process 1248
Started with run options --seed 41256
2/2: [===================================] 100% Time: 00:00:03, Time: 00:00:03
Finished in 3.66436s
2 tests, 10 assertions, 0 failures, 0 errors, 0 skips
同様に、app/views/users/show_follow.html.erb
に以下の欠落がある場合でも、Railsチュートリアル本文のリスト 14.29に記述されているテストは成功してしまいます。
<% provide(:title, @title) %>
<div class="row">
<aside class="col-md-4">
<section class="user_info">
...略
</section>
<section class="stats">
<%= render'shared/stats' %>
<% if @users.any? %>
<div class="user_avatars">
<% @users.each do |user|%>
<%= link_to gravatar_for(user, size: 30), user %>
<% end %>
</div>
<% end %>
</section>
</aside>
<div class="col-md-8">
<h3><%= @title %></h3>
<% if @users.any? %>
<ul class="users follow">
- <%= render @users %>
</ul>
<%= will_paginate %>
<% end %>
</div>
</div>
不具合の原因は、テストの実装が「ユーザーのプロフィールページへのリンクが1つ以上あればOK」という内容になっているためです。単一ユーザーのプロフィールページへのリンクは、「サイドバーのアイコンで1つ、FollowingまたはFollowersの一覧で1つ〜2つ1」存在します。そのため、「リンクが1つ以上」というテストの実装では、「サイドバー」「FollowingまたはFollowersの一覧」いずれか片方の欠落ではテストをすり抜けてしまうのです。
following
アクションおよびfollowers
アクションの統合テストの不具合を修正する
ユーザーのプロフィールページへのリンクの存在によって「サイドバー」「FollowingまたはFollowersの一覧」両方が正しく描画されていることを確認するためには、「ユーザーのプロフィールページへのリンクが2つ以上存在すること」をテストする必要があります。assert_select
で「要素が2つ以上存在すること」をテストするためには、オプションハッシュにminimum: 2
という設定を与えればOKです。
上記を踏まえ、test/integration/following_test.rb
の修正内容は以下のようになります。
require 'test_helper'
class FollowingTest < ActionDispatch::IntegrationTest
def setup
@user = users(:rhakurei)
log_in_as(@user)
end
test "following page" do
get following_user_path(@user)
assert_not @user.following.empty?
assert_match @user.following.count.to_s, response.body
@user.following.each do |user|
- assert_select "a[href=?]", user_path(user)
+ assert_select "a[href=?]", user_path(user), minimum: 2
end
end
test "followers page" do
get followers_user_path(@user)
assert_not @user.followers.empty?
assert_match @user.followers.count.to_s, response.body
@user.followers.each do |user|
- assert_select "a[href=?]", user_path(user)
+ assert_select "a[href=?]", user_path(user), minimum: 2
end
end
end
上記修正は本当に正しいのか
上記修正を行ったtest/integration/following_test.rb
を対象に、改めて以下のコードのテストを行ってみます。
<% provide(:title, @title) %>
<div class="row">
<aside class="col-md-4">
<section class="user_info">
...略
</section>
<section class="stats">
<%= render'shared/stats' %>
<% if @users.any? %>
<div class="user_avatars">
<% @users.each do |user|%>
<%= link_to gravatar_for(user, size: 30), user %>
<% end %>
</div>
<% end %>
</section>
</aside>
<div class="col-md-8">
<h3><%= @title %></h3>
<% if @users.any? %>
<ul class="users follow">
- <%= render @users %>
</ul>
<%= will_paginate %>
<% end %>
</div>
</div>
結果は以下のようになります。
# rails test test/integration/following_test.rb
Running via Spring preloader in process 1235
Started with run options --seed 28029
FAIL["test_followers_page", FollowingTest, 2.6110920999926748]
test_followers_page#FollowingTest (2.61s)
Expected at least 2 elements matching "a[href="/users/919532091"]", found 1..
Expected 1 to be >= 2.
test/integration/following_test.rb:23:in `block (2 levels) in <class:FollowingTest>'
test/integration/following_test.rb:22:in `block in <class:FollowingTest>'
FAIL["test_following_page", FollowingTest, 2.699444500001846]
test_following_page#FollowingTest (2.70s)
Expected at least 2 elements matching "a[href="/users/314048677"]", found 1..
Expected 1 to be >= 2.
test/integration/following_test.rb:14:in `block (2 levels) in <class:FollowingTest>'
test/integration/following_test.rb:13:in `block in <class:FollowingTest>'
2/2: [===================================] 100% Time: 00:00:02, Time: 00:00:02
Finished in 2.71399s
2 tests, 8 assertions, 2 failures, 0 errors, 0 skips
Expected 1 to be >= 2.
というのがポイントですね。「ユーザーのプロフィールページへのリンクが2つ以上存在すること」に対する正しいテストが書けているようです。
-
ログインユーザーがAdmin属性である場合、ユーザーを削除するためのリンクも、リンク先のURLは当該ユーザーのプロフィールページへのリンクのURLと同じになります。違うのは、発行されるアクションが
GET
であるかDELETE
であるかです。 ↩