14.2 [Follow]のWebインターフェイス
- フォロー/フォロー解除の基本的なインターフェイスを実装する
- フォローしているユーザーと、フォロワーにそれぞれ表示用のページを作成する
14.2.1 フォローのサンプルデータ
演習 1
コンソールを開き、User.first.followers.countの結果がリスト 14.14で期待している結果と合致していることを確認してみましょう。
>> User.first.followers.count
(2.2ms) SELECT sqlite_version(*)
User Load (2.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
(2.5ms) SELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = ? [["followed_id", 1]]
=> 38
演習 2
先ほどの演習と同様に、User.first.following.countの結果も合致していることを確認してみましょう。
>> User.first.following.count
User Load (2.0ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
(2.5ms) SELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? [["follower_id", 1]]
=> 49
14.2.2 統計と[Follow]フォーム
- プロフィールページとHomeページを更新して、これを反映する
.
.
.
resources :users do
member do
get :following, :followers
# GET /users/1/following
# GET /users/1/followiers
end
end
.
.
.
<% @user ||= current_user %>
<%# @userがnilではない場合、@userにcurrent_userを代入する %>
<div class="stats">
<a href="<%= following_user_path(@user) %>">
<strong id="following" class="stat">
<%= @user.following.count %>
</strong>
following
</a>
<a href="<%= followers_user_path(@user) %>">
<strong id="followers" class="stat">
<%= @user.followers.count %>
</strong>
followers
</a>
</div>
<% unless current_user?(@user) %>
<%# current_user == @userでなければ %>
<div id="follow_form">
<% if current_user.following?(@user) %>
<%= render 'unfollow' %>
<% else %>
<%= render 'follow' %>
<% end %>
</div>
<% end %>
演習 1
ブラウザから /users/2 にアクセスし、フォローボタンが表示されていることを確認してみましょう。同様に、/users/5 では[Unfollow]ボタンが表示されているはずです。さて、/users/1 にアクセスすると、どのような結果が表示されるでしょうか?
Followボタン、 Unfollowボタン自体が無い
演習 2
ブラウザからHomeページとプロフィールページを表示してみて、統計情報が正しく表示されているか確認してみましょう。
確認のみなので省略
演習 3
Homeページに表示されている統計情報に対してテストを書いてみましょう。ヒント: リスト 13.28で示したテストに追加してみてください。同様にして、プロフィールページにもテストを追加してみましょう。
require 'test_helper'
class SiteLayoutTest < ActionDispatch::IntegrationTest
.
.
.
test "layout links when loged in" do
log_in_as(@user)
get root_path
assert_template 'static_pages/home'
assert_select "a[href=?]", users_path
assert_select "a[href=?]", user_path(@user)
assert_select "a[href=?]", edit_user_path(@user)
assert_select "a[href=?]", logout_path
assert_select "strong#following"
# <strong id="following">があるか
assert_match @user.following.count.to_s, response.body
# <body>に@user.following.countを文字列にしたものがあるか
assert_select "strong#followers"
# <strong id="following">があるか
assert_match @user.followers.count.to_s, response.body
# <body>に@user.followers.countを文字列にしたものがあるか
end
end
require 'test_helper'
class UsersProfileTest < ActionDispatch::IntegrationTest
include ApplicationHelper
def setup
@user = users(:michael)
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_select "strong#following"
# <strong id="following">があるか
assert_match @user.following.count.to_s, response.body
# <body>に@user.following.countを文字列にしたものがあるか
assert_select "strong#followers"
# <strong id="following">があるか
assert_match @user.followers.count.to_s, response.body
# <body>に@user.followers.countを文字列にしたものがあるか
assert_match @user.microposts.count.to_s, response.body
assert_select 'div.pagination', count: 1
@user.microposts.paginate(page: 1).each do |micropost|
assert_match micropost.content, response.body
end
end
end
14.2.3 [Following]と[Followers]ページ
require 'test_helper'
class FollowingTest < ActionDispatch::IntegrationTest
def setup
@user = users(:michael)
log_in_as(@user)
# michaelでログイン
end
test "following page" do
get following_user_path(@user)
# followingページにアクセス
assert_not @user.following.empty?
# フォローしているはず
assert_match @user.following.count.to_s, response.body
# <body>にフォロー数が表示されているはず
@user.following.each do |user|
assert_select "a[href=?]", user_path(user)
# フォローのリンクがあるはず
end
end
test "followers page" do
get followers_user_path(@user)
# followersページにアクセス
assert_not @user.followers.empty?
# フォロワーがいるはず
assert_match @user.followers.count.to_s, response.body
# <body>にフォロワー数が表示されているはず
@user.followers.each do |user|
assert_select "a[href=?]", user_path(user)
# フォロワーのリンクがあるはず
end
end
end
演習 1
ブラウザから /users/1/followers と /users/1/following を開き、それぞれが適切に表示されていることを確認してみましょう。サイドバーにある画像は、リンクとしてうまく機能しているでしょうか?
確認だけなので省略。
演習 2
リスト 14.29のassert_selectに関連するコードをコメントアウトしてみて、テストが正しく red に変わることを確認してみましょう。
確認のみなので省略。
14.2.4 [Follow]ボタン(基本編)
演習 1
ブラウザ上から /users/2 を開き、[Follow]と[Unfollow]を実行してみましょう。うまく機能しているでしょうか?
うまく機能していました。
演習 2
先ほどの演習を終えたら、Railsサーバーのログを見てみましょう。フォロー/フォロー解除が実行されると、それぞれどのテンプレートが描画されているでしょうか?
Relationship Destroy (13.6ms) DELETE FROM "relationships" WHERE "relationships"."id" = ? [["id", 88]]
Relationship Create (9.9ms) INSERT INTO "relationships" ("follower_id", "followed_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["follower_id", 1], ["followed_id", 3], ["created_at", "2021-03-21 12:05:09.044437"], ["updated_at", "2021-03-21 12:05:09.044437"]]
14.2.5 [Follow]ボタン(Ajax編)
- Ajaxを使って非同期(ページを移動せずに)でリクエストを送信する
演習 1
ブラウザから /users/2 にアクセスし、うまく動いているかどうか確認してみましょう。
うまく動いていました。
演習 2
先ほどの演習で確認が終わったら、Railsサーバーのログを閲覧し、フォロー/フォロー解除を実行した直後のテンプレートがどうなっているか確認してみましょう。
Unfollow→Follow
Rendered relationships/create.js.erb
Follow→Unfollow
Rendered relationships/destroy.js.erb
14.2.6 フォローをテストする
- バグを検知するためのシンプルなテストを書いていく
演習 1
リスト 14.36のrespond_toブロック内の各行を順にコメントアウトしていき、テストが正しくエラーを検知できるかどうか確認してみましょう。実際、どのテストケースが落ちたでしょうか?
format.html { redirect_to @user }
は落ちる。
format.js
は落ちない。
演習 2
リスト 14.40のxhr: trueがある行のうち、片方のみを削除するとどういった結果になるでしょうか? このとき発生する問題の原因と、なぜ先ほどの演習で確認したテストがこの問題を検知できたのか考えてみてください。
test "should follow a user with Ajax" do
assert_difference '@user.following.count', 1 do
# post relationships_path, xhr: true, params: { followed_id: @other.id }
end
end
FAIL["test_should_follow_a_user_with_Ajax", #<Minitest::Reporters::Suite:0x00007f5a9ffae870 @name="FollowingTest">, 4.709730960999991]
test_should_follow_a_user_with_Ajax#FollowingTest (4.71s)
"@user.following.count" didn't change by 1.
Expected: 3
Actual: 2
test/integration/following_test.rb:46:in `block in <class:FollowingTest>'
ブロック内で何も処理が行われておらず@user.following.count
に変化がないためにエラーが起こる。