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 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:
# * <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 allow_value("isbn 1 2345 6789 0").for(:isbn) }
#
def allow_value(value)
AllowValueMatcher.new(value)
def allow_value(*values)
raise ArgumentError.new("need at least one argument") if values.empty?
AllowValueMatcher.new(*values)
end
class AllowValueMatcher # :nodoc:
include Helpers
def initialize(value)
@value = value
def initialize(*values)
@values_to_match = values
end
def for(attribute)
@ -39,8 +43,12 @@ module Shoulda # :nodoc:
if Symbol === @expected_message
@expected_message = default_error_message(@expected_message, :model_name => @instance.class.to_s.underscore, :attribute => @attribute)
end
@values_to_match.each do |value|
@value = value
@instance.send("#{@attribute}=", @value)
!errors_match?
return false if errors_match?
end
true
end
def failure_message
@ -52,7 +60,12 @@ module Shoulda # :nodoc:
end
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
private

View File

@ -17,6 +17,14 @@ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher do
it "should not allow a bad value" do
@model.should_not allow_value("xyz").for(:attr)
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
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)
end
bad_values = [nil, "", "abc", "0", "50001", "123456"]
bad_values = [nil, "", "abc", "0", "50001", "123456", []]
bad_values.each do |value|
it "should not allow a bad value (#{value.inspect})" do
@model.should_not allow_value(value).for(:attr)
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
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