Added AR extension error_messages_for to enable model.error_messages_for(:attribute).should for better validation testing #324

Closed
wants to merge 1 commit into
from
Jump to file or symbol
Failed to load files and symbols.
+207 −0
Split
@@ -0,0 +1,122 @@
+Feature: error_messages_on
+
+ Scenario: without validation for an attribute
+ Given a file named "spec/models/widget_spec.rb" with:
+ """
+ require "spec_helper"
+
+ class ValidatingWidget < ActiveRecord::Base
+ set_table_name :widgets
+ end
+
+ describe ValidatingWidget do
+ it "has no validation error message for non existing attribute (using error_message_on)" do
+ ValidatingWidget.new.error_message_on(:foo).should be_blank
+ ValidatingWidget.new.error_message_on(:foo).should be_nil
+ end
+
+ it "has no validation error message non existing attribute (using error_messages_on)" do
+ ValidatingWidget.new.error_messages_on(:foo).should be_blank
+ ValidatingWidget.new.error_messages_on(:foo).should be_nil
+ end
+ end
+ """
+ When I run "rspec spec/models/widget_spec.rb"
+ Then the examples should all pass
+
+ Scenario: with no validation errors for an attribute
+ Given a file named "spec/models/widget_spec.rb" with:
+ """
+ require "spec_helper"
+
+ class ValidatingWidget < ActiveRecord::Base
+ set_table_name :widgets
+ validates_presence_of :name
+ end
+
+ describe ValidatingWidget do
+ it "has no validation error message (using error_message_on)" do
+ ValidatingWidget.new(:name => 'Yo').error_message_on(:name).should be_blank
+ ValidatingWidget.new(:name => 'Yo').error_message_on(:name).should be_nil
+ end
+
+ it "has no validation error message (using error_messages_on)" do
+ ValidatingWidget.new(:name => 'Yo').error_messages_on(:name).should be_blank
+ ValidatingWidget.new(:name => 'Yo').error_messages_on(:name).should be_nil
+ end
+ end
+ """
+ When I run "rspec spec/models/widget_spec.rb"
+ Then the examples should all pass
+
+ Scenario: with no validation errors for an attribute but other attributes fail validation
+ Given a file named "spec/models/widget_spec.rb" with:
+ """
+ require "spec_helper"
+
+ class ValidatingWidget < ActiveRecord::Base
+ set_table_name :widgets
+ validates_presence_of :name
+ end
+
+ describe ValidatingWidget do
+ it "has no validation error message (using error_message_on)" do
+ ValidatingWidget.new.error_message_on(:foo).should be_blank
+ ValidatingWidget.new.error_message_on(:foo).should be_nil
+ end
+
+ it "has no validation error message (using error_messages_on)" do
+ ValidatingWidget.new.error_messages_on(:foo).should be_blank
+ ValidatingWidget.new.error_messages_on(:foo).should be_nil
+ end
+ end
+ """
+ When I run "rspec spec/models/widget_spec.rb"
+ Then the examples should all pass
+
+ Scenario: with one validation error for an attribute
+ Given a file named "spec/models/widget_spec.rb" with:
+ """
+ require "spec_helper"
+
+ class ValidatingWidget < ActiveRecord::Base
+ set_table_name :widgets
+ validates_presence_of :name, :message => 'our error'
+ end
+
+ describe ValidatingWidget do
+ it "has validation error message (using error_message_on)" do
+ ValidatingWidget.new.error_message_on(:name).should == 'our error'
+ end
+
+ it "has validation error message (using error_messages_on)" do
+ ValidatingWidget.new.error_messages_on(:name).should == 'our error'
+ end
+ end
+ """
+ When I run "rspec spec/models/widget_spec.rb"
+ Then the examples should all pass
+
+ Scenario: with two validation errors for an attribute
+ Given a file named "spec/models/widget_spec.rb" with:
+ """
+ require "spec_helper"
+
+ class ValidatingWidget < ActiveRecord::Base
+ set_table_name :widgets
+ validates_length_of :name, :within => 10..20, :too_long => "pick a shorter name", :too_short => "pick a longer name"
+ validates_format_of :name, :with => /^[a-zA-Z]*$/, :message => "can only contain letters"
+ end
+
+ describe ValidatingWidget do
+ it "has validation error message array (using error_message_on)" do
+ ValidatingWidget.new(:name => '1').error_message_on(:name).should eq([ 'pick a longer name','can only contain letters'])
+ end
+
+ it "has validation error message array (using error_messages_on)" do
+ ValidatingWidget.new(:name => '1').error_messages_on(:name).should eq([ 'pick a longer name','can only contain letters'])
+ end
+ end
+ """
+ When I run "rspec spec/models/widget_spec.rb"
+ Then the examples should all pass
@@ -37,4 +37,34 @@ def errors_on(attribute)
[self.errors[attribute]].flatten.compact
end
alias :error_on :errors_on
+
+ # :call-seq:
+ # model.error_message_on(:attribute).should be_blank
+ # model.error_message_on(:attribute).should == 'msg'
+ # model.error_messages_on(:attribute).should == ['msg1', 'msg2']
+ #
+ # Extension to enhance AR Model instances to ensure the correct validation
+ # rule executed by testing the validation message itself, instead of the
+ # attribute's error count.
+ #
+ # Equivalent to, yet sounds more natural, than:
+ # model.errors[:attribute].should include('msg')
+ #
+ # Reasoning: If there are two or more validations for :attribute, each
+ # should be tested. Simply calling model.should have(1).error_on(:attribute)
+ # could yield false positives if the wrong validation fires. It's better to
+ # check that the proper validation was triggered via the validation message
+ #
+ # For zero or one error message, the AR infused array is dropped, making the
+ # test should more natural (e.g. == 'msg' instead of eq(['msg'])
+ #
+ # Calls model.valid? in order to prepare the object's errors
+ # object.
+ def error_messages_on(attribute)
+ return nil if self.valid? # Model valid
+ return nil if self.errors[attribute].empty? # Model invalid, this attribute is valid
+ return self.errors[attribute][0] if self.errors[attribute].length == 1 # Attribute invalid, one error msg
+ self.errors[attribute] # Attribute invalid, two+ error msg
+ end
+ alias :error_message_on :error_messages_on
end
@@ -0,0 +1,55 @@
+require "spec_helper"
+
+describe "error_messages_on" do
+ let(:klass) do
+ Class.new do
+ include ActiveModel::Validations
+ end
+ end
+
+ it "calls valid?" do
+ model = klass.new
+ model.should_receive(:valid?)
+ model.errors_on(:foo)
+ end
+
+ it "returns nil on attribute when the entire model is valid" do
+ model = klass.new
+ model.error_message_on(:bar).should be_nil
+ model.error_message_on(:bar).should be_blank
+ end
+
+ it "returns nil on attribute when model is invalid but this attribute is valid" do
+ model = klass.new
+ model.stub(:errors) do
+ { :foo => ['a'], :bar => [] }
+ end
+ model.error_message_on(:bar).should be_nil
+ model.error_message_on(:bar).should be_blank
+ end
+
+ it "returns error message on attribute when there is one error message for it" do
+ model = klass.new
+ model.stub(:errors) do
+ { :foo => ['a'] }
+ end
+ model.error_message_on(:foo).should == 'a'
+ end
+
+ it "returns error message array on attribute when there are two error messages for it" do
+ model = klass.new
+ model.stub(:errors) do
+ { :foo => ['a', 'b'] }
+ end
+ model.error_messages_on(:foo).should eq(['a','b'])
+ end
+
+ it "returns error message array on attribute when there are more than two error messages for it" do
+ model = klass.new
+ model.stub(:errors) do
+ { :foo => ['a', 'b', 'c'] }
+ end
+ model.error_messages_on(:foo).should eq(['a','b','c'])
+ end
+
+end