Change `validate_uniqueness_of_matcher` to give non-null columns default values

Previously, if a model had an attribute that mapped to a non-nullable database
column that was not under test the matcher would fail with an SQL error like:

    examples.column may not be NULL #...

With this change the matcher attempts to provide a default value for all
non-nullable, non-primary attributes. This should avoid the above error in
simple cases.
This commit is contained in:
Peter Marsh 2014-02-10 22:52:40 +00:00 committed by Elliot Winkler
parent 5b44edf8b3
commit 561ac3e2f0
3 changed files with 51 additions and 1 deletions

View File

@ -22,6 +22,9 @@
* You cannot test that your attribute allows nil (`.in_array([nil])`) if
the column does not allow null values.
* Change `validate_uniqueness_of(...)` so that it provides default values for
non-nullable attributes.
# v 2.5.0
* Fix Rails/Test::Unit integration to ensure that the test case classes we are

View File

@ -115,6 +115,11 @@ module Shoulda # :nodoc:
@subject.class.new.tap do |instance|
instance.__send__("#{@attribute}=", value)
other_non_nullable_columns.each do |non_nullable_column|
instance.__send__("#{non_nullable_column.name}=", correct_type_for_column(non_nullable_column))
end
if has_secure_password?
instance.password = 'password'
instance.password_confirmation = 'password'
@ -193,7 +198,7 @@ module Shoulda # :nodoc:
end
def correct_type_for_column(column)
if column.type == :string
if column.type == :string || column.type == :binary
'0'
elsif column.type == :datetime
DateTime.now
@ -215,6 +220,12 @@ module Shoulda # :nodoc:
end
value
end
def other_non_nullable_columns
@subject.class.columns.select do |column|
column.name != @attribute && !column.null && !column.primary
end
end
end
end
end

View File

@ -341,6 +341,42 @@ describe Shoulda::Matchers::ActiveModel::ValidateUniquenessOfMatcher do
end
end
context "a model with non-nullable attribute" do
context "of type" do
[:string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |type|
context type do
it "does not raise an error" do
model = define_model_with_non_nullable(type)
expect { expect(model).to matcher }.not_to raise_error
end
end
end
end
context "that is a primary key" do
it "does not cause duplicate entry errors by re-using default values for primary keys" do
create_table :examples, id: false do |t|
t.string :attr
t.integer :non_nullable, primary: true
end
model_class = define_model(:example, attr: :string) do
validates_uniqueness_of :attr
end
model_1 = model_class.new
model_2 = model_class.new
expect(model_1).to matcher
expect { expect(model_2).to matcher }.not_to raise_error
end
end
def define_model_with_non_nullable(type)
define_model(:example, attr: :string, non_nullable: { type: type, options: { null: false } }) do
attr_accessible :attr, :non_nullable
validates_uniqueness_of :attr
end.new
end
end
def case_sensitive_validation_with_existing_value(attr_type)
model = define_model(:example, attr: attr_type) do
attr_accessible :attr