1. リスト 14.36のrespond_to
ブロック内の各行を順にコメントアウトしていき、テストが正しくエラーを検知できるかどうか確認してみましょう。実際、どのテストケースが落ちたでしょうか?
RelationshipsController#create
において、format.html
以下の行が欠落している場合
RelationshipsController#create
に以下の変更を加えた場合の動作から見てみましょう。
def create
@user = User.find(params[:followed_id])
current_user.follow(@user)
respond_to do |format|
- format.html { redirect_to @user }
format.js
end
end
上記変更を適用した上で、test/integration/following_test.rb
を対象としてテストを実行した結果は以下です。
# rails test test/integration/following_test.rb
Running via Spring preloader in process 1590
Started with run options --seed 42916
ERROR["test_should_follow_a_user_the_standard_way", FollowingTest, 3.4918121000082465]
test_should_follow_a_user_the_standard_way#FollowingTest (3.49s)
ActionController::UnknownFormat: ActionController::UnknownFormat: ActionController::UnknownFormat
app/controllers/relationships_controller.rb:7:in `create'
test/integration/following_test.rb:30:in `block (2 levels) in <class:FollowingTest>'
test/integration/following_test.rb:29:in `block in <class:FollowingTest>'
6/6: [===================================] 100% Time: 00:00:03, Time: 00:00:03
Finished in 3.89168s
6 tests, 13 assertions, 0 failures, 1 errors, 0 skips
エラー内容がActionController::UnknownFormat
であることがポイントです。上記エラーログでは端折られていますが、今回発生したActionController::UnknownFormat
エラーのより詳細なエラーメッセージは以下のようになります。
ActionController::UnknownFormat: RelationshipsController#create is missing a template for this request format and variant.
request.formats: ["text/html"]
request.variant: []
「RelationshipsController#create is missing a template(略)」とありますね。create
コントローラーに対するビューのテンプレート、ファイル名としてはapp/views/relationships/create.html.erb
が存在しないためのエラーです。
RelationshipsController#create
において、format.js
以下の行が欠落している場合
続いて、RelationshipsController#create
に以下の変更を加えた場合の動作です。
def create
@user = User.find(params[:followed_id])
current_user.follow(@user)
respond_to do |format|
format.html { redirect_to @user }
- format.js
end
end
上記変更を適用した上で、test/integration/following_test.rb
を対象としてテストを実行した結果は以下です。
# rails test test/integration/following_test.rb
Running via Spring preloader in process 1604
Started with run options --seed 30420
6/6: [===================================] 100% Time: 00:00:03, Time: 00:00:03
Finished in 3.26699s
6 tests, 14 assertions, 0 failures, 0 errors, 0 skips
テストは正常に完了します。テスト「should follow a user with Ajax」が落ちることを期待していたのですが、違いますね。
RelationshipsController#destroy
において、format.html
以下の行が欠落している場合
今度はRelationshipsController#destroy
に以下の変更を加えた場合の動作です。
def destroy
@user = Relationship.find(params[:id]).followed
current_user.unfollow(@user)
respond_to do |format|
- format.html { redirect_to @user }
format.js
end
end
上記変更を適用した上で、test/integration/following_test.rb
を対象としてテストを実行した結果は以下です。
# rails test test/integration/following_test.rb
Running via Spring preloader in process 559
Started with run options --seed 5514
ERROR["test_should_unfollow_a_user_the_standard_way", FollowingTest, 4.025347000000693]
test_should_unfollow_a_user_the_standard_way#FollowingTest (4.03s)
ActionController::UnknownFormat: ActionController::UnknownFormat: ActionController::UnknownFormat
app/controllers/relationships_controller.rb:16:in `destroy'
test/integration/following_test.rb:46:in `block (2 levels) in <class:FollowingTest>'
test/integration/following_test.rb:45:in `block in <class:FollowingTest>'
6/6: [===================================] 100% Time: 00:00:04, Time: 00:00:04
Finished in 4.16709s
6 tests, 13 assertions, 0 failures, 1 errors, 0 skips
ActionController::UnknownFormat
エラーでテストが落ちています。「RelationshipsController#create
において、format.html
以下の行が欠落している場合」と同様の挙動ですね。
RelationshipsController#destroy
において、format.js
以下の行が欠落している場合
最後はRelationshipsController#destroy
に以下の変更を加えた場合の動作です。
def destroy
@user = Relationship.find(params[:id]).followed
current_user.unfollow(@user)
respond_to do |format|
format.html { redirect_to @user }
- format.js
end
end
上記変更を適用した上で、test/integration/following_test.rb
を対象としてテストを実行した結果は以下です。
# rails test test/integration/following_test.rb
NOTE: Gem::Specification#rubyforge_project= is deprecated with no replacement. It will be removed on or after 2019-12-01.
Gem::Specification#rubyforge_project= called from /usr/local/bundle/specifications/i18n-0.9.5.gemspec:17.
NOTE: Gem::Specification#rubyforge_project= is deprecated with no replacement. It will be removed on or after 2019-12-01.
Gem::Specification#rubyforge_project= called from /usr/local/bundle/specifications/i18n-0.9.5.gemspec:17.
Running via Spring preloader in process 573
Started with run options --seed 63825
6/6: [===================================] 100% Time: 00:00:04, Time: 00:00:04
Finished in 4.95860s
6 tests, 14 assertions, 0 failures, 0 errors, 0 skips
テストは正常に完了します。
発展 -
2. リスト 14.40のxhr: true
がある行のうち、片方のみを削除するとどういった結果になるでしょうか? このとき発生する問題の原因と、なぜ先ほどの演習で確認したテストがこの問題を検知できたのか考えてみてください。
test/integration/following_test.rb
の内容を以下のように改変した場合、テストの結果はどうなるでしょうか。「テスト『should follow a user with Ajax』において、xhr: true
がある行を削除する」という改変です。
require 'test_helper'
class FollowingTest < ActionDispatch::IntegrationTest
def setup
@user = users(:rhakurei)
@other = users(:mkirisame)
log_in_as(@user)
end
...略
test "should follow a user the standard way" do
assert_difference '@user.following.count', 1 do
post relationships_path, params: { followed_id: @other.id }
end
end
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
test "should unfollow a user the standard way" do
@user.follow(@other)
relationship = @user.active_relationships.find_by(followed_id: @other.id)
assert_difference '@user.following.count', -1 do
delete relationship_path(relationship)
end
end
test "should unfollow a user with Ajax" do
@user.follow(@other)
relationship = @user.active_relationships.find_by(followed_id: @other.id)
assert_difference '@user.following.count', -1 do
delete relationship_path(relationship), xhr: true
end
end
end
上記テストの実行結果は以下のようになります。
# rails test test/integration/following_test.rb
Running via Spring preloader in process 586
Started with run options --seed 63280
FAIL["test_should_follow_a_user_with_Ajax", FollowingTest, 1.9365238999998837]
test_should_follow_a_user_with_Ajax#FollowingTest (1.94s)
"@user.following.count" didn't change by 1.
Expected: 3
Actual: 2
test/integration/following_test.rb:36:in `block in <class:FollowingTest>'
6/6: [===================================] 100% Time: 00:00:04, Time: 00:00:04
Finished in 4.76945s
6 tests, 14 assertions, 1 failures, 0 errors, 0 skips
上記テストでは、テスト「should follow a user with Ajax」において、POST
リクエスト自体が発行されていません。POST
リクエストが発行されないことには、RelationshipsController#create
メソッドも実行されません。当該RelationshipsController#create
の内容は以下の通りです。
def create
@user = User.find(params[:followed_id])
current_user.follow(@user)
respond_to do |format|
format.html { redirect_to @user }
format.js
end
end
RDB上のデータにフォローを反映する処理は、上記コードのうちcurrent_user.follow(@user)
となります。 current_user.follow(@user)
が実行されなければ、@user.following.count
の値も変化することはありません。結果、「@user.following.count
の値が想定と違う」という理由でテストが落ちることになります。
逆に、「テスト『should unfollow a user with Ajax』において、xhr: true
がある行を削除する」という改変を加えた場合(下記ソース)も、同様の形でテストが落ちます。
require 'test_helper'
class FollowingTest < ActionDispatch::IntegrationTest
def setup
@user = users(:rhakurei)
@other = users(:mkirisame)
log_in_as(@user)
end
...略
test "should follow a user the standard way" do
assert_difference '@user.following.count', 1 do
post relationships_path, params: { followed_id: @other.id }
end
end
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
test "should unfollow a user the standard way" do
@user.follow(@other)
relationship = @user.active_relationships.find_by(followed_id: @other.id)
assert_difference '@user.following.count', -1 do
delete relationship_path(relationship)
end
end
test "should unfollow a user with Ajax" do
@user.follow(@other)
relationship = @user.active_relationships.find_by(followed_id: @other.id)
assert_difference '@user.following.count', -1 do
- delete relationship_path(relationship), xhr: true
end
end
end
上記テストの実行結果は以下のようになります。
# rails test test/integration/following_test.rb
Running via Spring preloader in process 599
Started with run options --seed 27288
FAIL["test_should_unfollow_a_user_the_standard_way", FollowingTest, 4.007498499999201]
test_should_unfollow_a_user_the_standard_way#FollowingTest (4.01s)
"@user.following.count" didn't change by -1.
Expected: 2
Actual: 3
test/integration/following_test.rb:45:in `block in <class:FollowingTest>'
6/6: [===================================] 100% Time: 00:00:04, Time: 00:00:04
Finished in 4.10546s
6 tests, 14 assertions, 1 failures, 0 errors, 0 skips
今度は、テスト「should unfollow a user with Ajax」中でDELETE
リクエストが発行されなくなりました。RelationshipsController#destroy
中にあるcurrent_user.unfollow(@user)
という処理が実行されず、@user.following.count
の数が変化しなかった結果、テストが落ちたのですね。
def destroy
@user = Relationship.find(params[:id]).followed
current_user.unfollow(@user)
respond_to do |format|
format.html { redirect_to @user }
format.js
end
end
発展 - FollowingTest
で、より適切なテストを行えるようにしてみる
Railsチュートリアル本文リスト 14.40のテストでは、例えばcreate.js.erb
やdestroy.js.erb
の記述内容にまで踏み込んだテストができていませんでした。別記事「Railsチュートリアル 第14章 ユーザーをフォローする - 演習「フォローをテストする」 - FollowingTestの問題点と、その改良」にて、当該テストの問題点や改良策について記述しています。