Shoulda-matchers: validates_uniqueness_of does not work when record is new and attribute is an association

Created on 29 May 2014  Â·  12Comments  Â·  Source: thoughtbot/shoulda-matchers

When using validates_uniqueness_of tests on join tables, an AssociationTypeMismatch is raised because shoulda-matchers tries to assign a string to the association instead of the association's actual type.

Here's a db/schema.rb with a join table:

  create_table "organization_memberships", id: false, force: true do |t|
    t.integer  "organization_id", null: false
    t.integer  "user_id",         null: false
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  add_index "organization_memberships", ["organization_id", "user_id"], name: "index_organization_memberships_on_organization_id_and_user_id", unique: true, using: :btree

And here's the corresponding model:

require 'active_record'
class OrganizationMembership < ActiveRecord::Base
  belongs_to :user
  belongs_to :organization

  validates :user,
    :presence => true,
    :uniqueness => { :scope => :organization_id }

  validates :organization,
    :presence => true,
    :uniqueness => { :scope => :user_id }
end

And here's a test:

it "enforces uniqueness" do
  expect(subject).to validate_uniqueness_of(:user).scoped_to(:organization_id)
end

Unfortunately, this test will fail:

     Failure/Error: expect(subject).to validate_uniqueness_of(:user).scoped_to(:organization_id)
     ActiveRecord::AssociationTypeMismatch:
       User(#70186039298000) expected, got String(#70186016877360)

That's because on this line, a string is assigned instead of an object of the association's type.

Expected result: The validation procedure should assign an object of the association's correct type, rather than an arbitrary string.

Bug PR Welcome

Most helpful comment

As a workaround, one can define subject with a valid persisted object :

# with factory_girl
subject { create(:user) }

# without
subject { User.create(email: '[email protected]', password: '1234', …) }

All 12 comments

Just hit this myself, womp womp. Strangely sometimes it works, sometimes it doesn't.

This is happening to me too.

Yep, same here.

Yep - I got this bug as well.
Any workaround or proposed solution?

With some digging the above mentioned line should be at:
https://github.com/thoughtbot/shoulda-matchers/tree/0e2e9c45199ca567dce91b218f1c3d39ce7fe55a/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb#L322

I hit the same issue, if I explicitly create the item it works, so possible workaround

it "enforces uniqueness" do
  expect(create(:organization_membership)).to validate_uniqueness_of(:user).scoped_to(:organization_id)
end

I'm wondering if this is less about the fact that the model is a join table, and more about the fact that the validation is on an association. I don't think validate_uniqueness_of explicitly supports that...

Just hit this myself as well.

Using @hwatkins's workaround for now.

@mcmire You are right in saying that the docs do not mention associations explicitly for uniqueness validation, but specifying associations in the validation does indeed work in Rails.

Just ran into this myself. Since no one has explicitly said it yet: the solution was (for both me and the op) to test :user_id instead of :user.

As a workaround, one can define subject with a valid persisted object :

# with factory_girl
subject { create(:user) }

# without
subject { User.create(email: '[email protected]', password: '1234', …) }

Hey folks. In an effort to lighten our load as maintainers and be able to serve you better in the future, the shoulda-matchers team is working on cleaning out the cobwebs in this repo by pruning the backlog. As there are few of us, there are a lot of items that will simply never earn our attention in a reasonable time frame, and rather than giving you an empty promise, we think it makes more sense to focus on more recent issues. That means, unfortunately, that we must close this issue.

Don't take this the wrong way: our aim is not to diminish the effort people have made or dismiss problems that have been raised. If you feel that we should reopen this issue, then please let us know so that we can reprioritize it. Thanks!

Was this page helpful?
0 / 5 - 0 ratings