Dan Rubio
Dan Rubio

Reputation: 4907

How to properly use shoulda matchers with uniqueness validation?

I am trying rspec and shoulda matchers as my new testing framework and I'm a little confused as to how to get the should validate_uniqueness_of(:attribute) test to pass. Here is my simple model:

class Employee < ActiveRecord::Base
  validates :name, presence: true
  validates :title, presence: true
  validates :department, presence: true
  validates :avatar, presence: true 
  validates :email, presence: true, uniqueness: true
  validates :reports_to, presence: true
  mount_uploader :avatar, EmployeeAvatarUploader, on: :file_name
end

And here is the initial test:

RSpec.describe Employee, type: :model do
  it { should validate_presence_of(:name) }
  it { should validate_presence_of(:title) }
  it { should validate_presence_of(:department) }
  it { should validate_presence_of(:avatar) }
  it { should validate_presence_of(:email) }
  it { should validate_uniqueness_of(:email) } 
  it { should validate_presence_of(:reports_to) }
end

This fails with a description of the error pasted below:

`1) Employee should validate uniqueness of email
 Failure/Error: should validate_uniqueness_of(:email)

 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 "title" violates not-null constraint
     DETAIL:  Failing row contains (21, null, null, null, null, null, null, 2017-06-27 01:07:49.125445, 2017-06-27 01:07:49.125445, null, null).
     : INSERT INTO "employees" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id"

   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.`

So my impression from the documentation was that my test should work as is. From the shoulda matcher documentation, I've tried this version of my test:

RSpec.describe Employee, type: :model do
  it { should validate_presence_of(:name) }
  it { should validate_presence_of(:title) }
  it { should validate_presence_of(:department) }
  it { should validate_presence_of(:avatar) }
  it { should validate_presence_of(:email) }
  it 'should validate uniqueness of email' do
    email1 = FactoryGirl.create(:account, email: '[email protected]')
    email2 = FactoryGirl.build(:account, email: '[email protected]' )
    should validate_uniqueness_of(:email)
  end
  it { should validate_presence_of(:reports_to) }
end

This didn't seem to work either. Then I tried a variation of this test found by clicking Rspec validates_uniqueness_ofthis link

But still no luck. Could someone please direct me to the proper way of making this validation work?

Upvotes: 10

Views: 7281

Answers (2)

Shiva
Shiva

Reputation: 12514

describe Employee, type: :model do
  context 'validations' do
    it { should validate_uniqueness_of(:email) }
  end
end

Here, subject is implicit. A new instance of Employee is built(not created) and validation is verified.

Upvotes: 0

treiff
treiff

Reputation: 1306

You should be able to call it this way:

describe "validations" do
  subject { Employee.new(title: "Here is the content") }
  it { should validate_uniqueness_of(:email) }
end

Or with FactoryGirl:

describe "validations" do
  subject { FactoryGirl.build(:employee) }
  it { should validate_uniqueness_of(:email) }
end

Your employees table has a database level constraint requiring title, so you need to create a valid employee object first.

It looks as though in your second example you're building a account instead of employee.

See docs here: docs

Upvotes: 20

Related Questions