Get validates_uniqueness_of to work better with `NOT NULL` columns

Instead of having to persist the subject, like so:

  describe 'validations' do
    subject { build(:model) }
    it { validates_presence_of :required_attr }
    context "persisted" do
      # Needs to be persisted to prevent NOT NULL db error
      subject { create(:model) }
      it { validates_uniqueness_of :unique_attr }
    end
  end

You can now just write:

  describe 'validations' do
    subject { build(:model) }
    it { validates_presence_of :required_attr }
    it { validates_uniqueness_of :unique_attr }
  end

When required the matcher saves the record to the database.
This commit is contained in:
Maarten Claes 2014-08-07 16:03:28 +02:00 committed by Elliot Winkler
parent dbf4900bf7
commit 6128af94fa
3 changed files with 36 additions and 5 deletions

View File

@ -18,6 +18,12 @@
* Fix `delegate_method` so that it can be required independent of Active
Support.
* Fix `validate_uniqueness_of`. When used against an unpersisted record whose
model contained a non-nullable column other than the one being validated, the
matcher would break. Even if the test set that column to a value beforehand,
the record had to be persisted in order for the matcher to work. Now this is
no longer the case and the record can remain unpersisted.
### Improvements
* `have_and_belongs_to_many` now checks to make sure that the join table

View File

@ -209,6 +209,7 @@ module Shoulda
end
def matches?(subject)
@original_subject = subject
@subject = subject.class.new
@expected_message ||= :taken
set_scoped_attributes &&
@ -253,16 +254,20 @@ module Shoulda
value = 'a'
end
@subject.class.new.tap do |instance|
@original_subject.tap do |instance|
instance.__send__("#{@attribute}=", value)
if has_secure_password?
instance.password = 'password'
instance.password_confirmation = 'password'
end
ensure_secure_password_set(instance)
instance.save(validate: false)
end
end
def ensure_secure_password_set(instance)
if has_secure_password?
instance.password = "password"
instance.password_confirmation = "password"
end
end
def has_secure_password?
@subject.class.ancestors.map(&:to_s).include?('ActiveModel::SecurePassword::InstanceMethodsOnActivation')
end

View File

@ -47,6 +47,26 @@ describe Shoulda::Matchers::ActiveModel::ValidateUniquenessOfMatcher do
expect(Example.count).to eq 0
expect(validating_uniqueness_with_other).to matcher
end
context "and the table uses NOT NULL columns" do
let(:model_class) do
define_model(
:example,
attr: :string,
required_attr: { type: :string, options: { null: false } }
) do
attr_accessible :attr, :required_attr
validates_presence_of :required_attr
validates_uniqueness_of :attr
end
end
it "works if the subject to sets the required attibutes" do
model = model_class.new(required_attr: "foo")
expect(Example.count).to eq 0
expect(model).to matcher
end
end
end
def define_model_with_other(options = {})