オブジェクトが1減るか? shared_example : 小林研 Rails Tips (29)

はじめに

Rails Tips の 29 回目です。今日は destroy の request_spec を説明します。テストの雛形は以下のようになります。

  # describe "DELETE #destroy" do
  #   subject { -> { delete <%= singular_table_name %>_path(one) } }
  #   context "owned object" do
  #     let(:one) { <%= singular_table_name %> }
  #     it_behaves_like "レスポンスコード確認", 302
  #     it_behaves_like "オブジェクトが1減るか?", <%= class_name %>
  #     it_behaves_like "リダイレクト確認"
  #     it_behaves_like "Notice メッセージ確認", "XXXを削除しました。"
  #   end

  #   context "now owned object" do
  #     let(:one) { not_mine }
  #     it_behaves_like "レスポンスコード確認", 302
  #     it_behaves_like "rootリダイレクト確認"
  #   end
  # end

オブジェクト属性が変化しない?

以下は昨日の緊急連絡先 (EmergencyCall) の destroy の spec になります。権限のある emergency_call と 権限のない not_mine で one の let を切り替えることで両方のテストをしています。権限がある方は、303 (See other) のレスポンスコードを返し、リダイレクト確認および Notice メッセージを確認します。レスポンスコードを 303 に設定しないと、Turbo の場合別のオブジェクトが削除されてしまうので注意が必要です。destroy の場合、オブジェクトが一つ減るため、今回の「オブジェクトが1減るか?」を呼び出します。

    describe "DELETE #destroy" do
      subject { -> { delete emergency_call_path(one) } }
      context "owned object" do
        let(:one) { emergency_call }
        it_behaves_like "レスポンスコード確認", 303
        it_behaves_like "オブジェクトが1減るか?", EmergencyCall
        it_behaves_like "リダイレクト確認"
        it_behaves_like "Notice メッセージ確認", "緊急連絡先を削除しました。"
      end

      context "now owned object" do
        let(:one) { not_mine }
        it_behaves_like "レスポンスコード確認", 302
        it_behaves_like "rootリダイレクト確認"
      end
    end

この shared_example は以下のようになります。オブジェクトが1増えるかの反対に一つ減ることを確認するだけです。

shared_examples_for "オブジェクトが1減るか?" do |klass|
  it "#{klass}.count が 1 つ減ること" do
    expect { subject.call }.to change(klass, :count).by(-1)
  end
end

正しく動作させるための実装

権限の確認を行い、自分のオブジェクトでなければ welcome_path にリダイレクトします。権限がある場合にはオブジェクトを削除し、see_other ステータスでリダイレクトします。

  def destroy#
    @emergency_call, @ids = get_parameters_and_ids [EmergencyCall.gp(true)]
    @gakusei = @emergency_call.gakusei
    @gnumber = @gakusei.gnumber_year_is(@now_year)
    redirect_to welcomes_path_with_message and return unless my_gnumber?(@gnumber) || pr_kyoumu? || charged_gnumber?

    @emergency_call.destroy
    redirect_to gakusei_path(@gakusei), notice: notice_message(EmergencyCall), status: :see_other
  end

おわりに

これで一通り request spec で使っている shared example を全て紹介しました。次回からは普段使っている helper_method を中心に紹介していきます。