0

I'm fairly new to Rspec testing and this is very frustrating.

I have a model which uses validates_uniqueness_of to prevent duplicate entries from being created. (I know that this is not guaranteed and the preferred way is to use database level constraints but that is not relevant at the moment).

The problem is that my Rspec test seems to indicate that it is possible to create 2 entries in the table with identical user_id and board_id even though in practice, in the console and in the app itself, this cannot be done.

models/moderator_join.rb

class ModeratorJoin < ActiveRecord::Base
  belongs_to :user
  belongs_to :board

  validates :user_id, presence: true
  validates :board_id, presence: true

  validates_uniqueness_of :user_id, scope: :board_id, message: "is already a moderator of that board."
end

spec/models/moderator_join_spec.rb

describe ModeratorJoin do
  let(:user) { create(:user) } // Create with FactoryGirl
  let(:board) { create(:board) } // Create with FactoryGirl
  let(:join) { ModeratorJoin.create(user: user, board: board) }

  subject { join }

  it { should be_valid } // Test passes 
  its(:id) { should_not eq nil } // Test passes
  its(:user) { should eq user } // Test passes
  its(:board) { should eq board } // Test passes

  describe "user/board pairs" do
    let(:join2) { ModeratorJoin.new(user: user, board: board) }

    it "must be unique" do
        expect(join2).to_not be_valid // TEST FAILS
    end
  end
end 

console output

Failures:
1) ModeratorJoin user/board pairs must be unique
Failure/Error: expect(join2).to_not be_valid
expected #<ModeratorJoin id: nil, user_id: 121, board_id: 1, created_at: nil, updated_at: nil> not to be valid

 # ./spec/models/moderator_join_spec.rb:39:in `block (3 levels) in <top (required)>'
Adam
  • 518
  • 7
  • 17

1 Answers1

1

I think (can't check myself now) that subject is not executed in your test. Try this variant

it "must be unique" do
  subject
  expect(join2).to_not be_valid // TEST FAILS
end
gotva
  • 5,919
  • 2
  • 25
  • 35
  • That made the test pass! But I have no idea why... I thought that the *join* object would be visible inside the "must be unique" test because it was declared at the start. – Adam Oct 06 '14 at 18:59
  • "visible" and "executed" is a different things. `subject`, `let` have "lazy" loading strategy. If it is not called - it is not executed. idea is described [here](https://www.relishapp.com/rspec/rspec-core/v/2-6/docs/helper-methods/let-and-let) – gotva Oct 06 '14 at 19:03
  • Ah! Of course! I forgot about lazy loading. So, the other solution is to use *let!* instead of *let*. Thank you. – Adam Oct 06 '14 at 19:08