I have 2 model: Answer and Book.
Although they are very similar, all tests pass to Answer but 1 test fails for Book
models/answer.rb
class Answer < ApplicationRecord
belongs_to :question, class_name: "Question", foreign_key: "question_id"
validates :text, presence: true
validates :correct, presence: true
validates :question_id, presence: true
end
models/book.rb
class Book < ApplicationRecord
belongs_to :grade, class_name: "Grade", foreign_key: "grade_id"
validates :name, presence: true, uniqueness: { case_sensitive: false }
validates :grade_id, presence: true
end
spec/factories/answers.rb
FactoryBot.define do
factory :answer do
text { Faker::Book.author }
correct { %i(false, true).sample }
question
end
end
spec/factories/books.rb
FactoryBot.define do
factory :book do
name { Faker::Book.title }
grade
end
end
spect/models/answer_spec.rb
require 'rails_helper'
RSpec.describe Answer, type: :model do
describe 'validation' do
it { should validate_presence_of(:text) }
it { should validate_presence_of(:correct) }
it { should validate_presence_of(:question_id) }
it { should belong_to :question }
end
end
spect/models/book_spec.rb
require 'rails_helper'
RSpec.describe Book, type: :model do
describe 'validation' do
it { should validate_presence_of(:name) }
it { should validate_uniqueness_of(:name).case_insensitive }
it { should validate_presence_of(:grade_id) }
it { should belong_to :grade }
end
end
The message I get in return is:
Book validation is expected to validate that :name is case-insensitively unique Failure/Error: it { should validate_uniqueness_of(:name).case_insensitive }
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: **PG::NotNullViolation: ERROR: null value in column "grade_id" violates not-null constraint** DETAIL: Failing row contains (2, null, null, 2021-12-23 15:03:39.977355, 2021-12-23 15:03:39.977355). 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.
I have tried many ways that I came up with or saw in stack/git posts but none seams to work.
CodePudding user response:
This situation is covered in the validates_uniqueness_of docs.
If there is no existing record, it will create one using the record you provide.
Your spec doesn't say how to create a Book, and it doesn't know that it needs a Grade. You need to tell it, it won't use the factory. Do this by adding a subject for your one-liners to use.
require 'rails_helper'
RSpec.describe Book, type: :model do
subject { build(:book) }
describe 'validation' do
it { should validate_presence_of(:name) }
it { should validate_uniqueness_of(:name).case_insensitive }
it { should belong_to :grade }
end
end
Note that it { should validate_presence_of(:grade_id) }
is redundant with it { should belong_to :grade }
. belongs_to already validates presence.
CodePudding user response:
When trying test uniqueness in a model that has a reference you need to provide one to be compare first.
For doing so you should specify the subject:
describe 'validation' do
subject{
}
After that you can call FactoryBot to build the one to be compere to:
FactoryBot.build(:book)
Final snipet:
describe 'validation' do
subject{
FactoryBot.build(:book)
}
Mr. [Schwern][1] Enhanced the code a little better:
describe 'validation' do
subject{ build(:book)}