【Rspec】bad_request (400) but it was :internal_server_error (500)
はじめに
- 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
である
ここで仮説とはいえないまでも当たりをつけます🎯
-
schedules/create_form
に対してテストを行なっているため、こちらに何らかの原因がありそう... - さらに範囲を絞ると、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