LoginSignup
2
0

More than 3 years have passed since last update.

【ShouldaMatchers】複数カラムへの重複チェックで発生したエラーの原因と対応方法

Posted at

事象

RspecでShouldaMatchersを使用した複数カラムの重複チェック実行時、以下のようなDBのエラーが発生しました。

 1) Like is expected to validate that :user_id is case-sensitively unique within the scope of :post_id
     Failure/Error: it { is_expected.to validate_uniqueness_of(:user_id).scoped_to(:post_id) }

     Shoulda::Matchers::ActiveRecord::ValidateUniquenessOfMatcher::ExistingRecordInvalid:
       validate_uniqueness_of works by matching a new record against an
       existing record. If there is no existing record, it will create one
       using the record you provide.

       While doing this, the following error was raised:

         Mysql2::Error: Field 'user_id' doesn't have a default value

       The best way to fix this is to provide the matcher with a record where
       any required attributes are filled in with valid values beforehand.
     # ./spec/models/like_spec.rb:8:in `block (2 levels) in <main>'
     # -e:1:in `<main>'
     # ------------------
     # --- Caused by: ---
     # Mysql2::Error:
     #   Field 'user_id' doesn't have a default value
     #   ./spec/models/like_spec.rb:8:in `block (2 levels) in <main>'

※投稿に対してのいいね機能のテストとして、UserPostを保持するLikeモデルを作成していました。

spec/models/like_spec.rb
RSpec.describe Like, type: :model do
  it { is_expected.to validate_presence_of(:user_id) }
  it { should belong_to(:user) }
  it { is_expected.to validate_uniqueness_of(:user_id).scoped_to(:post_id) } # エラー発生

  it { is_expected.to validate_presence_of(:post_id) }
  it { should belong_to(:post) }
end
app/models/like.rb
class Like < ApplicationRecord
  belongs_to :user
  belongs_to :post
  validates :user_id, presence: true, uniqueness: { scope: :post_id }
  validates :post_id, presence: true
end

原因

uniquenessに対してテストをするvalidate_uniqueness_ofは、インスタンスが存在しない場合は作成してテスト実行するため、その他のカラムのDBの制約によってはDBエラーが発生するようです。
今回の場合だと、user_idに対してnullを許容しないケースとなっていたため、nullのデータが作成されてしまい、エラーが発生しました。

対応

spec/models/like_spec.rbに、インスタンスを事前に作成するような記述を追加することで、テストが問題なく完了しました。
ShouldaMathcersを使用したモデルのテストではインスタンス等不要だと思っていましたが、作成が必要なケースがあることも認識しておく必要があります。

spec/models/like_spec.rb(修正後)
RSpec.describe Like, type: :model do
  subject { FactoryBot.build(:like) } # 追加
  it { is_expected.to validate_presence_of(:user_id) }
  it { should belong_to(:user) }
  it { is_expected.to validate_uniqueness_of(:user_id).scoped_to(:post_id) }

  it { is_expected.to validate_presence_of(:post_id) }
  it { should belong_to(:post) }
end

参考

モジュール:Shoulda :: Matchers :: ActiveRecord — YARD0.8.7.3によるドキュメント

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