#事象
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>'
※投稿に対してのいいね機能のテストとして、User
とPost
を保持する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によるドキュメント