allow multiple values for AllowValueMatcher

With the deprecation of "should_allow_values_for" there is currently no clean
way to specify multiple allowed (or forbidden) values for an attribute in a
single assertion. This can make test cases unnecessarily verbose.

This patch gives AllowValueMatcher the ability to take multiple values. The new
matcher is fully downward compatible (i.e. given a single argument it behaves
identically to the old matcher).

This matcher will stop checking for bad values when the first bad value is
encountered.
This commit is contained in:
Frank Luithle 2010-09-05 17:33:32 +02:00 committed by Gabe Berke-Williams
parent c86d8e2e0c
commit cab62a1697
2 changed files with 50 additions and 9 deletions

View File

@ -2,7 +2,10 @@ module Shoulda # :nodoc:
module Matchers module Matchers
module ActiveModel # :nodoc: module ActiveModel # :nodoc:
# Ensures that the attribute can be set to the given value. # Ensures that the attribute can be set to the given value or values. If
# multiple values are given the match succeeds only if all given values
# are allowed. Otherwise, the matcher fails at the first bad value in the
# argument list (the remaining arguments are not processed then).
# #
# Options: # Options:
# * <tt>with_message</tt> - value the test expects to find in # * <tt>with_message</tt> - value the test expects to find in
@ -13,15 +16,16 @@ module Shoulda # :nodoc:
# it { should_not allow_value('bad').for(:isbn) } # it { should_not allow_value('bad').for(:isbn) }
# it { should allow_value("isbn 1 2345 6789 0").for(:isbn) } # it { should allow_value("isbn 1 2345 6789 0").for(:isbn) }
# #
def allow_value(value) def allow_value(*values)
AllowValueMatcher.new(value) raise ArgumentError.new("need at least one argument") if values.empty?
AllowValueMatcher.new(*values)
end end
class AllowValueMatcher # :nodoc: class AllowValueMatcher # :nodoc:
include Helpers include Helpers
def initialize(value) def initialize(*values)
@value = value @values_to_match = values
end end
def for(attribute) def for(attribute)
@ -39,8 +43,12 @@ module Shoulda # :nodoc:
if Symbol === @expected_message if Symbol === @expected_message
@expected_message = default_error_message(@expected_message, :model_name => @instance.class.to_s.underscore, :attribute => @attribute) @expected_message = default_error_message(@expected_message, :model_name => @instance.class.to_s.underscore, :attribute => @attribute)
end end
@instance.send("#{@attribute}=", @value) @values_to_match.each do |value|
!errors_match? @value = value
@instance.send("#{@attribute}=", @value)
return false if errors_match?
end
true
end end
def failure_message def failure_message
@ -52,7 +60,12 @@ module Shoulda # :nodoc:
end end
def description def description
"allow #{@attribute} to be set to #{@value.inspect}" "allow #{@attribute} to be set to " <<
if @values_to_match.length > 1
"any of [#{@values_to_match.map {|v| v.inspect }.join(', ')}]"
else
@values_to_match.first.inspect
end
end end
private private

View File

@ -17,6 +17,14 @@ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher do
it "should not allow a bad value" do it "should not allow a bad value" do
@model.should_not allow_value("xyz").for(:attr) @model.should_not allow_value("xyz").for(:attr)
end end
it "should allow several good values" do
@model.should allow_value("abcde", "deabc").for(:attr)
end
it "should not allow several bad values" do
@model.should_not allow_value("xyz", "zyx", nil, []).for(:attr)
end
end end
context "an attribute with a format validation and a custom message" do context "an attribute with a format validation and a custom message" do
@ -51,12 +59,32 @@ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher do
@model.should allow_value("12345").for(:attr) @model.should allow_value("12345").for(:attr)
end end
bad_values = [nil, "", "abc", "0", "50001", "123456"] bad_values = [nil, "", "abc", "0", "50001", "123456", []]
bad_values.each do |value| bad_values.each do |value|
it "should not allow a bad value (#{value.inspect})" do it "should not allow a bad value (#{value.inspect})" do
@model.should_not allow_value(value).for(:attr) @model.should_not allow_value(value).for(:attr)
end end
end end
it "should not allow bad values (#{bad_values.map {|v| v.inspect}.join(', ')})" do
@model.should_not allow_value(*bad_values).for(:attr)
end
end end
context "an AllowValueMatcher with multiple values" do
before { @matcher = allow_value("foo", "bar").for(:baz) }
it "should describe itself" do
@matcher.description.should eq('allow baz to be set to any of ["foo", "bar"]')
end
end
context "an AllowValueMatcher with a single value" do
before { @matcher = allow_value("foo").for(:baz) }
it "should describe itself" do
@matcher.description.should eq('allow baz to be set to "foo"')
end
end
end end