2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

RSpecでバリデーションを無視したい

Last updated at Posted at 2025-05-12

Rails 開発で RSpec を使用していると、Factorybot等で作成したオブジェクトやカラムに対してのバリデーションを一時的に無視したい時が現れます。
たとえばどういう時かというと、バリデーションを設定したときに、

  • 外部依存のAPIから取得すべき必須パラメータがまだモックできていない
  • CSV取り込み用の途中データをテストしたい

というときに、テストケースが全て落ちてしまいますよね。
そのようなときにうまくいった方法を書きます。

skip_callback を使用する

特定のバリデーションが、before_validationで設定されていた場合、skip_callbackを使用することで、バリデーションを一部スキップすることが可能です。
簡単なコードで表すと、

class Order < ApplicationRecord
  before_validation :validate_external_status

  private

  def validate_external_status
    # 外部APIでのステータス検証
    ExternalService.check(status_code)
  end
end

というモデルがあったとして、

RSpec.describe "Order" do
  let(:order) { build(:order, status_code: "PENDING") }

  context "外部ステータス検証をスキップしたいとき" do
    before do
      # validate_external_status コールバックだけを無効化
      Order.skip_callback(:validate, :before, :validate_external_status)
    end

    after do
      # コールバックを復元
      Order.set_callback(:validate, :before, :validate_external_status)
    end

    it "バリデーションなしで保存できる" do
      expect(order.save).to be true # passed
    end
  end
end

という具合に使用します。
他のテストへの影響を最小限にするため、テスト後に必ず set_callback で元へ戻すことで、一部のバリデーションをスキップし、他は通常通りテストを行うようにします。

FactoryBotの trait やオプションでバリデーションをスキップ

FactoryBot のtraitto_createオプションを組み合わせると、テスト用のファクトリでのみ save(validate: false)を実行するようにカスタマイズできます。

基本構文は、

trait :skip_validations do
  to_create { |instance| instance.save(validate: false) }
end

です。
name と email が必要な User モデルがある前提で使用してみると、

FactoryBot.define do
  factory :user do
    name { "ohtani" }
    email { "invalid_email" }  # 本来は format: のバリデーションがあると仮定

    trait :skip_validations do
      to_create { |user| user.save(validate: false) }
    end
  end
end

という感じになります。

この時点では何が起こるのかというと、

  • factory :userのデフォルト生成では通常どおりバリデーションが働く
  • create(:user, :skip_validations)とするとバリデーションを飛ばして保存される

実際のテストでは、

RSpec.describe "User" do
  let(:skipped_user) { create(:user, :skip_validations) } # バリデーションスキップ

  context "skip_validations" do
    it "persists the record without running validations" do
      expect(skipped_user).to be_persisted # passed
    end
  end
end

というふうに使えます。

save(validate: false) を使う

Rails の標準メソッド save(validate: false) は、モデルに定義された すべての バリデーションと関連コールバックをスキップしてレコードを保存します。

RSpec.describe "User" do
  let(:user) { User.new(email: "hogehogedaze") }  # 不正なメールフォーマット

  context "skip validation" do
    before { user.save(validate: false) }

    it "persists the record even with invalid email" do
      expect(user).to be_persisted
    end
  end
end

この方法は、Rails ガイドにも書いてあるように、save(validate: false)は非常に注意して使用する必要があります。

なぜなら、この方法は、すべてのバリデーションを無視することになるためです。DB制約以外は何も担保してくれません。
結果としてテストのデータの整合性が保たれない不十分なものになってしまいます。そのため、注意して使う必要があります。

最後に

テストの開発効率を高めるためにバリデーションを一時的にスキップするテクニックは便利ですが、「本番コードを汚さない」「テストの意図を明確化する」ことが重要です。ぜひ、今回の事例を参考にしてみてください!

2
1
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?