LoginSignup
0
0

More than 3 years have passed since last update.

Railsチュートリアル 第14章 ユーザーをフォローする - 演習「フォローをテストする」

Posted at

1. リスト 14.36respond_toブロック内の各行を順にコメントアウトしていき、テストが正しくエラーを検知できるかどうか確認してみましょう。実際、どのテストケースが落ちたでしょうか?

RelationshipsController#createにおいて、format.html以下の行が欠落している場合

RelationshipsController#createに以下の変更を加えた場合の動作から見てみましょう。

app/controllers/relationships_controller.rb#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に以下の変更を加えた場合の動作です。

app/controllers/relationships_controller.rb#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に以下の変更を加えた場合の動作です。

app/controllers/relationships_controller.rb#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に以下の変更を加えた場合の動作です。

app/controllers/relationships_controller.rb#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.40xhr: trueがある行のうち、片方のみを削除するとどういった結果になるでしょうか? このとき発生する問題の原因と、なぜ先ほどの演習で確認したテストがこの問題を検知できたのか考えてみてください。

test/integration/following_test.rbの内容を以下のように改変した場合、テストの結果はどうなるでしょうか。「テスト『should follow a user with Ajax』において、xhr: trueがある行を削除する」という改変です。

test/integration/following_test.rb
  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の内容は以下の通りです。

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がある行を削除する」という改変を加えた場合(下記ソース)も、同様の形でテストが落ちます。

test/integration/following_test.rb
  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の数が変化しなかった結果、テストが落ちたのですね。

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

発展 - FollowingTestで、より適切なテストを行えるようにしてみる

Railsチュートリアル本文リスト 14.40のテストでは、例えばcreate.js.erbdestroy.js.erbの記述内容にまで踏み込んだテストができていませんでした。別記事「Railsチュートリアル 第14章 ユーザーをフォローする - 演習「フォローをテストする」 - FollowingTestの問題点と、その改良」にて、当該テストの問題点や改良策について記述しています。

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