Rails 6.0 → 6.1 の移行にて、非UTF-8を含むPOSTリクエストに対する処理をrspec-railsのAnonymousControllerを使ってテストしている部分が ActionController::BadRequest で落ちるようになった問題とその解決について

解決方法の概要

  1. AnonymousController作成時、対象アクションのUTF-8検証をスキップさせる
    • skip_parameter_encoding アクション名
  2. 一時的に定数を設定する
    • AnonymousController = controller_class
  3. テスト実行後、定数を削除する
    • Object.send(:remove_const, :AnonymousController)

以降は経緯や考えたことなどを載せます。


Rails6.1の変更

Railsを 6.0 から 6.1 に更新した際、非UTF-8を含むPOSTリクエストをするテストケースが落ちてしまうことに気がつきました。
エラーメッセージは以下です。

ActionController::BadRequest:
    Invalid request parameters: Invalid encoding for parameter: ****

Rails 6.1 のCHANGELOG を確認すると、次のような記載がありました。

Catch invalid UTF-8 parameters for POST requests and respond with BadRequest.

この挙動は、skip_parameter_encoding の指定により部分的に無効化することができます。

例:ExampleControllerの create アクションのみ、エンコーディングのチェックをスキップさせる

class ExampleController < ApplicationController
  skip_parameter_encoding :create

通常のコントローラーであればこれで解決されるのですが、AnonymousControllerを使っているテストでは追加の対応が必要でした。

AnonymousControllerの場合

AnonymousControllerでは、skip_parameter_encodingを追加してもスキップが適用されず、BadRequestが返されました。

Railsの実装を追っていくと、この現象は以下のような流れで発生しているようでした。

  1. リクエストが飛んできた際にcontroller_class_forが呼ばれる。このとき、引数は"anonymous"
  2. このメソッド内では 引数.camelize + "Controller" の文字列から一致する定数のオブジェクトを得ようとするが、AnonymousController定数が存在しないためコントローラーのクラス取得に失敗する。
  3. コントローラークラスが不明なので、skip_parameter_encoding などの設定も反映できず、デフォルト動作(非UTF-8は弾く)となってしまう。

対策

今回は、”AnonymousController定数を作ってしまう”という方法で、クラス取得が成功するようにしました。

RSpec.describe テスト対象, type: :controller do
  controller(ApplicationController) do
    skip_parameter_encoding :対象アクション

    def 対象アクション
      # 動作
    end
  end

  AnonymousController = controller_class

また、作成した定数が他のテストに影響することを防ぐため、テスト終了時に定数を削除するようにしました。

    after(:all) do
      Object.send(:remove_const, :AnonymousController)
    end

Tags:

Categories:

Updated: