diff --git a/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb b/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb index f5f92c39f..4edc71e27 100644 --- a/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +++ b/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb @@ -89,7 +89,7 @@ def initialize(attribute) end def simple_description - "validate that #{@confirmation_attribute} matches #{@attribute}" + "validate that :#{@confirmation_attribute} matches :#{@attribute}" end def matches?(subject) @@ -103,30 +103,27 @@ def matches?(subject) private def disallows_different_value - set_confirmation('some value') - disallows_value_of('different value') do |matcher| - qualify_matcher(matcher) + qualify_matcher(matcher, 'some value') end end def allows_same_value - set_confirmation('same value') - allows_value_of('same value') do |matcher| - qualify_matcher(matcher) + qualify_matcher(matcher, 'same value') end end def allows_missing_confirmation - set_confirmation(nil) - allows_value_of('any value') do |matcher| - qualify_matcher(matcher) + qualify_matcher(matcher, nil) end end - def qualify_matcher(matcher) + def qualify_matcher(matcher, confirmation_attribute_value) + matcher.values_to_preset = { + confirmation_attribute => confirmation_attribute_value + } matcher.with_message( @expected_message, against: confirmation_attribute, @@ -134,12 +131,15 @@ def qualify_matcher(matcher) ) end - def set_confirmation(val) - setter = :"#{@confirmation_attribute}=" + def set_confirmation(value) + @last_value_set_on_confirmation_attribute = value - if @subject.respond_to?(setter) - @subject.__send__(setter, val) - end + AttributeSetter.set( + matcher_name: 'confirmation', + object: @subject, + attribute_name: confirmation_attribute, + value: value + ) end end end diff --git a/spec/support/unit/record_validating_confirmation_builder.rb b/spec/support/unit/record_validating_confirmation_builder.rb index 40d23bc48..57368536e 100644 --- a/spec/support/unit/record_validating_confirmation_builder.rb +++ b/spec/support/unit/record_validating_confirmation_builder.rb @@ -25,9 +25,8 @@ def message=(message) end def attribute_to_confirm - :attribute_to_confirm + options.fetch(:attribute, :attribute_to_confirm) end - alias_method :attribute, :attribute_to_confirm def confirmation_attribute :"#{attribute_to_confirm}_confirmation" @@ -44,11 +43,8 @@ def attribute_that_receives_error private def create_model - _attribute = attribute_to_confirm - _options = options - - define_model(model_name, _attribute => :string) do - validates_confirmation_of(_attribute, _options) + define_model(model_name, attribute_to_confirm => :string) do |model| + model.validates_confirmation_of(attribute_to_confirm, options) end end end diff --git a/spec/unit/shoulda/matchers/active_model/validate_confirmation_of_matcher_spec.rb b/spec/unit/shoulda/matchers/active_model/validate_confirmation_of_matcher_spec.rb index db7fae995..b651f81d4 100644 --- a/spec/unit/shoulda/matchers/active_model/validate_confirmation_of_matcher_spec.rb +++ b/spec/unit/shoulda/matchers/active_model/validate_confirmation_of_matcher_spec.rb @@ -6,7 +6,7 @@ context '#description' do it 'states that the confirmation must match its base attribute' do builder = builder_for_record_validating_confirmation - message = "validate that #{builder.confirmation_attribute} matches #{builder.attribute_to_confirm}" + message = "validate that :#{builder.confirmation_attribute} matches :#{builder.attribute_to_confirm}" matcher = described_class.new(builder.attribute_to_confirm) expect(matcher.description).to eq(message) end @@ -29,11 +29,65 @@ end end - context 'when the model does not have a confirmation validation' do - it 'fails' do - model = define_model(:example, attribute_to_confirm: :string) - record = model.new - expect(record).not_to validate_confirmation_of(:attribute_to_confirm) + context 'when the model does not have a confirmation attribute' do + it 'raises an AttributeDoesNotExistError' do + model = define_model(:example) + + assertion = lambda do + expect(model.new).to validate_confirmation_of(:attribute_to_confirm) + end + + message = <<-MESSAGE.rstrip +The matcher attempted to set :attribute_to_confirm_confirmation to "some +value" on the Example, but that attribute does not exist. + MESSAGE + + expect(&assertion).to raise_error( + Shoulda::Matchers::ActiveModel::AllowValueMatcher::AttributeDoesNotExistError, + message + ) + end + end + + context 'when the model does not have the attribute under test' do + it 'raises an AttributeDoesNotExistError' do + model = define_model(:example, attribute_to_confirm_confirmation: :string) + + assertion = lambda do + expect(model.new).to validate_confirmation_of(:attribute_to_confirm) + end + + message = <<-MESSAGE.rstrip +The matcher attempted to set :attribute_to_confirm to "different value" +on the Example, but that attribute does not exist. + MESSAGE + + expect(&assertion).to raise_error( + Shoulda::Matchers::ActiveModel::AllowValueMatcher::AttributeDoesNotExistError, + message + ) + end + end + + context 'when the model has all attributes, but does not have the validation' do + it 'fails with an appropriate failure message' do + model = define_model(:example, attribute_to_confirm: :string) do + attr_accessor :attribute_to_confirm_confirmation + end + + assertion = lambda do + expect(model.new).to validate_confirmation_of(:attribute_to_confirm) + end + + message = <<-MESSAGE +Example did not properly validate that +:attribute_to_confirm_confirmation matches :attribute_to_confirm. + After setting :attribute_to_confirm_confirmation to "some value", then + setting :attribute_to_confirm to "different value", the matcher + expected the Example to be invalid, but it was valid instead. + MESSAGE + + expect(&assertion).to fail_with_message(message) end end