🔥

【Rspec】bad_request (400) but it was :internal_server_error (500)

2024/04/10に公開

はじめに

  • Railsのテストを書いていて、タイトルのエラーが発生
  • 解決までのアプローチをメモ書きで共有
  • 同様のエラーに詰まった人に貢献できれば幸いです

開発環境

  • MacBook(Apple M3 Pro)
  • Cursor
  • Docker

いきなり結論

以下のバリデーションを追加して400番エラーにしてあげることで解決

validates :participant_limit, presence: true

エラー内容

具体的に確認していきましょう
以下のテストを実行

$ docker-compose run --rm app bundle exec rspec spec/requests/api/v1/admin/schedules/create_spec.rb

出力

Failures:

  1) Api::V1::Admin::Schedules::Create paramsが不正な場合 participant_limitがnilの場合 behaves like bad request response status bad_request
     Failure/Error: expect(response).to have_http_status(:bad_request)
       expected the response to have status code :bad_request (400) but it was :internal_server_error (500)
     (省略)

エラーメッセージから分かること

Api::V1::Admin::Schedules::Createのテストで期待されるHTTPステータスコードが:bad_request(400)であるにも関わらず、実際には:internal_server_error(500)が返されている

エラー原因の特定

let(:params) do
    {
      schedule: attributes_for(:schedule, :unpublished, :offline).merge(
        (省略)
        participant_limit: nil
      )
    }
  end

  it_behaves_like 'bad request'  # 500番エラーを出してる

実現したいこと

テストの目的

「participant_limitカラムがnilのとき、レコードが作成されない」というテストを書きたい

  let(:params) do
    {
      schedule: attributes_for(:schedule, :unpublished, :offline).merge(
        (省略)
        participant_limit: nil
      )
    }
  end

方針

nilはそのままだと500エラーになってしまう
これを400エラーにするハンドリング方法を探したい

解決までにやったこと

binding.pryを仕込んでparticipant_limitの中身を確認

requestが実行される直前、つまりAPIリクエストを送信する直前に挿入してみる

  it 'participant_limitのレコードが作成されない' do
    binding.pry
    expect { proc { request } }.to change(Schedule, :count).by(0)
  end

params と入力した際の出力

[1] pry(#<RSpec::ExampleGroups::ApiV1AdminSchedulesCreate::Params_2::ParticipantLimitNil>)> params
=> {:schedule=>
  {:prefecture_id=>38,
   : 〜
   :participant_limit=>nil,
   : 〜
   :is_schedule=>true}}

やはり、participant_limit=>nilである

ここで仮説とはいえないまでも当たりをつけます🎯

  1. schedules/create_formに対してテストを行なっているため、こちらに何らかの原因がありそう...
  2. さらに範囲を絞ると、formsのvalidationまわりで何かが不足していそう...(仮説)

以下、participant_limitカラムが存在しているか確認するバリデーションを追加

validate :participant_limit?, if: -> { participant_limit.present? }
validates :participant_limit, presence: true  # 追加

テストを走らせると成功🙌

20 examples, 0 failures

なぜエラーが解消されたか?

以下を追加することで、

validates :participant_limit, presence: true

participant_limitがnilである場合にバリデーションエラーが発生するようになった
これにより、participant_limitがnilの場合にはバリデーションが失敗し、400 Bad Requestのレスポンスが返されるようになる

以前は、participant_limitがnilの場合に内部でエラーが発生し、500 Internal Server Errorが返されていたが、このバリデーションルールの追加により、適切なエラーメッセージとともに400 Bad Requestが返されるようになり、テストが期待する挙動を満たすようになった

つまり、

participant_limitがnilの場合にバリデーションエラーを発生させるためにvalidates :participant_limit, presence: trueを追加したところ、期待通り400 Bad Requestのレスポンスが返されるようになった

おわりに

今回は、Rspecを追加していてよく遭遇する「bad_request (400) but it was :internal_server_error (500)」についてエラー解消までメモ書きしてきました。

「nilの場合はテストを通さない」というテストの時でも、validates :participant_limit, presence: trueを設けることで、participant_limitカラムがnilの場合にはバリデーションが失敗し、400 Bad Requestのレスポンスが返されるようにすることでテストが通りました。

今回の記事が皆さまのお役に立てれば幸いです。
ありがとうございました。

Discussion