Search code examples
ruby-on-railsactiverecordshoulda

Shoulda Matchers attempting to insert `nil` value while validating uniqueness


I have Referral model that tracks referrals (i.e. think when a user on a social media site generates a unique referral/invite link and invites others to join the app).

  • Referrer - one who sends out their unique invite link
  • Referree - one who receives and uses that link to sign up

My models and specs are laid out as follows

Model

# app/models/referral.rb

class Referral < ActiveRecord::Base
  # A referral has one user who refers and one user who is being referred. Both are User models.
  belongs_to :referrer, class_name: "User", foreign_key: "referrer_id"
  belongs_to :referree, class_name: "User", foreign_key: "referree_id"

  # Validate presence - don't allow nil
  validates :referrer_id, presence: true
  validates :referree_id, presence: true

  # Referrers can have multiple entries, must be unique to a referree, since they can 
  # only refer someone once. 
  # Referees can only BE referred once overall, so must be globally unique
  validates :referrer_id, uniqueness: { scope: :referree_id }
  validates :referree_id, uniqueness: true
end

Spec

# spec/models/referral_spec.rb
require "spec_helper"

RSpec.describe Referral, type: :model do
  describe "Associations" do
    it { should belong_to(:referrer) }
    it { should belong_to(:referree) }
  end

  describe "Validations" do
    it { should validate_presence_of(:referrer_id) }
    it { should validate_presence_of(:referree_id) }

    it { should_not allow_value(nil).for(:referrer_id) }
    it { should_not allow_value(nil).for(:referree_id) }

    # These two FAIL
    it { should validate_uniqueness_of(:referrer_id).scoped_to(:referree_id) }
    it { should validate_uniqueness_of(:referree_id) }
  end
end

My issue is that the last two spec tests always fail with something like

1) Referral Validations should require case sensitive unique value for referrer_id scoped to referree_id
   Failure/Error: it { should validate_uniqueness_of(:referrer_id).scoped_to(:referree_id) }
   ActiveRecord::StatementInvalid:
     PG::NotNullViolation: ERROR:  null value in column "referree_id" violates not-null constraint
     DETAIL:  Failing row contains (1, 2016-01-13 19:28:15.552112, 2016-01-13 19:28:15.552112, 0, null).
     : INSERT INTO "referrals" ("referrer_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"

It looks like the shoulda matchers are attempting to insert a nil value in there while testing various values. Playing with allow_nil being true and false didn't solve it.

Any idea why it might be stumbling there?

Thanks!


Solution

  • See this

    validate_uniqueness_of matcher creates a new instance if one does not exist already. While doing so, it may fail because of model validations on some other field. In your case, while validating uniqueness of referrer_id, value in referree_id was nil and hence it fails.

    Solution is to create an instance and assign values to the attributes which throws ERROR: null value in column.

    Again, refer the link in first line for example and detailed explanation