mirror of
https://github.com/thoughtbot/shoulda-matchers.git
synced 2022-11-09 12:01:38 -05:00
Tighten CouldNotSetAttributeError restriction
Why: * Previously, `allow_value` would raise a CouldNotSetAttributeError if the value being set didn't match the value the attribute had after being set, but only if the attribute was being changed from nil to non-nil or non-nil to nil. * It turns out it doesn't matter which value you're trying to set the attribute to -- if the attribute rejects that change it's confusing either way. (In fact, I was recently bit by a case in which I was trying to validate numericality of an attribute, where the writer method for that attribute was overridden to ensure that the attribute always stored a number and never contained non-number characters. This ended up making the numericality validation useless, of course -- but it caused confusion because the test acted in a way I didn't expect.) To satisfy the above: * `allow_value` now raises a CouldNotSetAttributeError if the attribute rejects the value being set in *any* way. * However, add a `ignoring_interference_by_writer` qualifier so that it is possible to manually override this behavior. * Fix tests that are failing now because of this new change: * Fix tests for allow_value matcher * Fix tests for numericality matcher * Remove tests for numericality matcher + integer column * An integer column will typecast any non-integer value to an integer. * Because of the typecasting, our tests for the numericality matcher against an integer column don't quite work, because we can't really test what happens when the attribute is set to a non-integer value. Now that `allow_value` is more strict, we're getting a CouldNotSetAttributeError when attempting to do so. * The tests mentioned were originally added to ensure that we are handling RangeErrors that ActiveRecord used to emit. This doesn't happen anymore, so the tests aren't necessary anymore either. * Fix tests for acceptance matcher * Fix tests for absence matcher
This commit is contained in:
parent
e6e81b9de0
commit
9d9dc4e6b9
12 changed files with 351 additions and 253 deletions
98
NEWS.md
98
NEWS.md
|
@ -53,24 +53,102 @@
|
|||
|
||||
* There are two changes to `allow_value`:
|
||||
|
||||
* The negative form of the matcher has been changed so that instead of
|
||||
* The negative form of `allow_value` has been changed so that instead of
|
||||
asserting that any of the given values is an invalid value (allowing good
|
||||
values to pass through), assert that *all* values are invalid values
|
||||
(allowing good values not to pass through). This means that this test which
|
||||
formerly passed will now fail:
|
||||
|
||||
expect(record).not_to allow_value('good value', *bad_values)
|
||||
``` ruby
|
||||
expect(record).not_to allow_value('good value', *bad_values)
|
||||
```
|
||||
|
||||
([19ce8a6])
|
||||
|
||||
* The matcher may raise an error if the attribute in question contains
|
||||
custom logic to ignore certain values, resulting in a discrepancy between
|
||||
the value you provide and the value that the attribute is actually set to.
|
||||
Specifically, if the attribute cannot be changed from a non-nil value to a
|
||||
nil value, or vice versa, then you'll get a CouldNotSetAttributeError. The
|
||||
current behavior (which is to permit this) is misleading, as the test that
|
||||
you're writing under the hood by using `allow_value` could be different from
|
||||
the test that actually ends up getting run. ([eaaa2d8])
|
||||
* `allow_value` now raises a CouldNotSetAttributeError if in setting the
|
||||
attribute, the value of the attribute from reading the attribute back is
|
||||
different from the one used to set it.
|
||||
|
||||
This would happen if the writer method for that attribute has custom logic
|
||||
to ignore certain incoming values or change them in any way. Here are three
|
||||
examples we've seen:
|
||||
|
||||
* You're attempting to assert that an attribute should not allow nil, yet
|
||||
the attribute's writer method contains a conditional to do nothing if
|
||||
the attribute is set to nil:
|
||||
|
||||
``` ruby
|
||||
class Foo
|
||||
include ActiveModel::Model
|
||||
|
||||
attr_reader :bar
|
||||
|
||||
def bar=(value)
|
||||
return if value.nil?
|
||||
@bar = value
|
||||
end
|
||||
end
|
||||
|
||||
describe Foo do
|
||||
it do
|
||||
foo = Foo.new
|
||||
foo.bar = "baz"
|
||||
# This will raise a CouldNotSetAttributeError since `foo.bar` is now "123"
|
||||
expect(foo).not_to allow_value(nil).for(:bar)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
* You're attempting to assert that an numeric attribute should not allow a
|
||||
string that contains non-numeric characters, yet the writer method for
|
||||
that attribute strips out non-numeric characters:
|
||||
|
||||
``` ruby
|
||||
class Foo
|
||||
include ActiveModel::Model
|
||||
|
||||
attr_reader :bar
|
||||
|
||||
def bar=(value)
|
||||
@bar = value.gsub(/\D+/, '')
|
||||
end
|
||||
end
|
||||
|
||||
describe Foo do
|
||||
it do
|
||||
foo = Foo.new
|
||||
# This will raise a CouldNotSetAttributeError since `foo.bar` is now "123"
|
||||
expect(foo).not_to allow_value("abc123").for(:bar)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
* You're passing a value to `allow_value` that the model typecasts into
|
||||
another value:
|
||||
|
||||
``` ruby
|
||||
describe Foo do
|
||||
# Assume that `attr` is a string
|
||||
# This will raise a CouldNotSetAttributeError since `attr` typecasts `[]` to `"[]"`
|
||||
it { should_not allow_value([]).for(:attr) }
|
||||
end
|
||||
```
|
||||
|
||||
With all of these failing examples, why are we making this change? We want
|
||||
to guard you (as the developer) from writing a test that you think acts one
|
||||
way but actually acts a different way, as this could lead to a confusing
|
||||
false positive or negative.
|
||||
|
||||
If you understand the problem and wish to override this behavior so that
|
||||
you do not get a CouldNotSetAttributeError, you can add the
|
||||
`ignoring_interference_by_writer` qualifier like so. Note that this will not
|
||||
always cause the test to pass.
|
||||
|
||||
``` ruby
|
||||
it { should_not allow_value([]).for(:attr).ignoring_interference_by_writer }
|
||||
```
|
||||
|
||||
([eaaa2d8])
|
||||
|
||||
* `validate_uniqueness_of` is now properly case-insensitive by default, to match
|
||||
the default behavior of the validation itself. This is a backward-incompatible
|
||||
|
|
|
@ -226,6 +226,7 @@ module Shoulda
|
|||
self.options = {}
|
||||
self.after_setting_value_callback = -> {}
|
||||
self.validator = Validator.new
|
||||
@ignoring_interference_by_writer = false
|
||||
end
|
||||
|
||||
def for(attribute)
|
||||
|
@ -255,6 +256,11 @@ module Shoulda
|
|||
self
|
||||
end
|
||||
|
||||
def ignoring_interference_by_writer
|
||||
@ignoring_interference_by_writer = true
|
||||
self
|
||||
end
|
||||
|
||||
def _after_setting_value(&callback)
|
||||
self.after_setting_value_callback = callback
|
||||
end
|
||||
|
@ -297,6 +303,10 @@ module Shoulda
|
|||
validator.attribute = attribute
|
||||
end
|
||||
|
||||
def ignoring_interference_by_writer?
|
||||
@ignoring_interference_by_writer
|
||||
end
|
||||
|
||||
def value_matches?(value)
|
||||
self.value = value
|
||||
set_attribute(value)
|
||||
|
@ -305,14 +315,14 @@ module Shoulda
|
|||
|
||||
def set_attribute(value)
|
||||
instance.__send__("#{attribute_to_set}=", value)
|
||||
ensure_that_attribute_has_been_changed_to_or_from_nil!(value)
|
||||
ensure_that_attribute_was_set!(value)
|
||||
after_setting_value_callback.call
|
||||
end
|
||||
|
||||
def ensure_that_attribute_has_been_changed_to_or_from_nil!(expected_value)
|
||||
def ensure_that_attribute_was_set!(expected_value)
|
||||
actual_value = instance.__send__(attribute_to_set)
|
||||
|
||||
if expected_value.nil? != actual_value.nil?
|
||||
if expected_value != actual_value && !ignoring_interference_by_writer?
|
||||
raise CouldNotSetAttributeError.create(
|
||||
instance.class,
|
||||
attribute_to_set,
|
||||
|
|
|
@ -31,6 +31,11 @@ module Shoulda
|
|||
self
|
||||
end
|
||||
|
||||
def ignoring_interference_by_writer
|
||||
@allow_matcher.ignoring_interference_by_writer
|
||||
self
|
||||
end
|
||||
|
||||
def failure_message
|
||||
@allow_matcher.failure_message_when_negated
|
||||
end
|
||||
|
|
|
@ -80,7 +80,11 @@ module Shoulda
|
|||
def submatchers
|
||||
@_submatchers ||=
|
||||
comparison_combos.map do |diff, submatcher_method_name|
|
||||
matcher = __send__(submatcher_method_name, @value + diff, nil)
|
||||
matcher = __send__(
|
||||
submatcher_method_name,
|
||||
(@value + diff).to_s,
|
||||
nil
|
||||
)
|
||||
matcher.with_message(@message, values: { count: @value })
|
||||
matcher
|
||||
end
|
||||
|
|
|
@ -8,9 +8,10 @@ module Shoulda
|
|||
|
||||
def initialize(attribute, options = {})
|
||||
@attribute = attribute
|
||||
@disallow_value_matcher = DisallowValueMatcher.new(NON_EVEN_NUMBER_VALUE).
|
||||
for(@attribute).
|
||||
with_message(:even)
|
||||
@disallow_value_matcher =
|
||||
DisallowValueMatcher.new(NON_EVEN_NUMBER_VALUE.to_s).
|
||||
for(@attribute).
|
||||
with_message(:even)
|
||||
end
|
||||
|
||||
def allowed_type
|
||||
|
|
|
@ -8,9 +8,10 @@ module Shoulda
|
|||
|
||||
def initialize(attribute, options = {})
|
||||
@attribute = attribute
|
||||
@disallow_value_matcher = DisallowValueMatcher.new(NON_ODD_NUMBER_VALUE).
|
||||
for(@attribute).
|
||||
with_message(:odd)
|
||||
@disallow_value_matcher =
|
||||
DisallowValueMatcher.new(NON_ODD_NUMBER_VALUE.to_s).
|
||||
for(@attribute).
|
||||
with_message(:odd)
|
||||
end
|
||||
|
||||
def allowed_type
|
||||
|
|
|
@ -7,7 +7,8 @@ module Shoulda
|
|||
NON_INTEGER_VALUE = 0.1
|
||||
def initialize(attribute)
|
||||
@attribute = attribute
|
||||
@disallow_value_matcher = DisallowValueMatcher.new(NON_INTEGER_VALUE).
|
||||
@disallow_value_matcher =
|
||||
DisallowValueMatcher.new(NON_INTEGER_VALUE.to_s).
|
||||
for(attribute).
|
||||
with_message(:not_an_integer)
|
||||
end
|
||||
|
|
|
@ -86,6 +86,7 @@ module Shoulda
|
|||
def matches?(subject)
|
||||
super(subject)
|
||||
@expected_message ||= :present
|
||||
|
||||
disallows_value_of(value, @expected_message)
|
||||
end
|
||||
|
||||
|
@ -103,21 +104,22 @@ module Shoulda
|
|||
else
|
||||
obj
|
||||
end
|
||||
elsif [Fixnum, Float].include?(attribute_class)
|
||||
1
|
||||
elsif attribute_class == BigDecimal
|
||||
BigDecimal.new(1, 0)
|
||||
elsif !attribute_class || attribute_class == String
|
||||
'an arbitrary value'
|
||||
else
|
||||
attribute_class.new
|
||||
case column_type
|
||||
when :integer, :float then 1
|
||||
when :decimal then BigDecimal.new(1, 0)
|
||||
when :datetime, :time, :timestamp then Time.now
|
||||
when :date then Date.new
|
||||
when :binary then "0"
|
||||
else 'an arbitrary value'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def attribute_class
|
||||
def column_type
|
||||
@subject.class.respond_to?(:columns_hash) &&
|
||||
@subject.class.columns_hash[@attribute.to_s].respond_to?(:klass) &&
|
||||
@subject.class.columns_hash[@attribute.to_s].klass
|
||||
@subject.class.columns_hash[@attribute.to_s].respond_to?(:type) &&
|
||||
@subject.class.columns_hash[@attribute.to_s].type
|
||||
end
|
||||
|
||||
def collection?
|
||||
|
|
|
@ -20,6 +20,10 @@ module Shoulda
|
|||
def message
|
||||
""
|
||||
end
|
||||
|
||||
def inspect
|
||||
%(#<#{self.class}: #{message}>)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -62,7 +62,9 @@ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher, type: :model do
|
|||
|
||||
it 'rejects several bad values' do
|
||||
expect(validating_format(with: /abc/)).
|
||||
not_to allow_value('xyz', 'zyx', nil, []).for(:attr)
|
||||
not_to allow_value('xyz', 'zyx', nil, []).
|
||||
for(:attr).
|
||||
ignoring_interference_by_writer
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -182,12 +184,18 @@ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher, type: :model do
|
|||
|
||||
it 'does not match given a bad value' do
|
||||
bad_values.each do |bad_value|
|
||||
expect(model).not_to allow_value(bad_value).for(:attr)
|
||||
expect(model).
|
||||
not_to allow_value(bad_value).
|
||||
for(:attr).
|
||||
ignoring_interference_by_writer
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not match given multiple bad values' do
|
||||
expect(model).not_to allow_value(*bad_values).for(:attr)
|
||||
expect(model).
|
||||
not_to allow_value(*bad_values).
|
||||
for(:attr).
|
||||
ignoring_interference_by_writer
|
||||
end
|
||||
|
||||
it "does not match given good values along with bad values" do
|
||||
|
@ -241,48 +249,139 @@ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher, type: :model do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when the attribute writer method ignores a non-nil value' do
|
||||
context 'when the attribute has a reader method' do
|
||||
it 'raises a CouldNotSetAttributeError' do
|
||||
model = define_active_model_class 'Example' do
|
||||
attr_reader :name
|
||||
context 'when the attribute interferes with attempts to be set' do
|
||||
context 'when the matcher has not been qualified with #ignoring_interference_by_writer' do
|
||||
context 'when the attribute cannot be changed from nil to non-nil' do
|
||||
it 'raises a CouldNotSetAttributeError' do
|
||||
model = define_active_model_class 'Example' do
|
||||
attr_reader :name
|
||||
|
||||
def name=(_value)
|
||||
nil
|
||||
def name=(_value)
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
assertion = -> {
|
||||
expect(model.new).to allow_value('anything').for(:name)
|
||||
}
|
||||
|
||||
expect(&assertion).to raise_error(
|
||||
described_class::CouldNotSetAttributeError
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
assertion = -> {
|
||||
expect(model.new).to allow_value('anything').for(:name)
|
||||
}
|
||||
context 'when the attribute cannot be changed from non-nil to nil' do
|
||||
it 'raises a CouldNotSetAttribute error' do
|
||||
model = define_active_model_class 'Example' do
|
||||
attr_reader :name
|
||||
|
||||
expect(&assertion).to raise_error(
|
||||
Shoulda::Matchers::ActiveModel::AllowValueMatcher::CouldNotSetAttributeError
|
||||
)
|
||||
def name=(value)
|
||||
@name = value unless value.nil?
|
||||
end
|
||||
end
|
||||
|
||||
record = model.new(name: 'some name')
|
||||
|
||||
assertion = -> {
|
||||
expect(record).to allow_value(nil).for(:name)
|
||||
}
|
||||
|
||||
expect(&assertion).to raise_error(
|
||||
described_class::CouldNotSetAttributeError
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the attribute cannot be changed from a non-nil value to another non-nil value' do
|
||||
it 'raises a CouldNotSetAttribute error' do
|
||||
model = define_active_model_class 'Example' do
|
||||
attr_reader :name
|
||||
|
||||
def name=(_value)
|
||||
@name = 'constant name'
|
||||
end
|
||||
end
|
||||
|
||||
record = model.new(name: 'some name')
|
||||
|
||||
assertion = -> {
|
||||
expect(record).to allow_value('another name').for(:name)
|
||||
}
|
||||
|
||||
expect(&assertion).to raise_error(
|
||||
described_class::CouldNotSetAttributeError
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the attribute writer method ignores a nil value' do
|
||||
context 'when the attribute has a reader method' do
|
||||
it 'raises a CouldNotSetAttribute error' do
|
||||
model = define_active_model_class 'Example' do
|
||||
attr_reader :name
|
||||
context 'when the matcher has been qualified with #ignoring_interference_by_writer' do
|
||||
context 'when the attribute cannot be changed from nil to non-nil' do
|
||||
it 'does not raise an error at all' do
|
||||
model = define_active_model_class 'Example' do
|
||||
attr_reader :name
|
||||
|
||||
def name=(value)
|
||||
@name = value unless value.nil?
|
||||
def name=(_value)
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
assertion = lambda do
|
||||
expect(model.new).
|
||||
to allow_value('anything').
|
||||
for(:name).
|
||||
ignoring_interference_by_writer
|
||||
end
|
||||
|
||||
expect(&assertion).not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
record = model.new(name: 'some name')
|
||||
context 'when the attribute cannot be changed from non-nil to nil' do
|
||||
it 'does not raise an error at all' do
|
||||
model = define_active_model_class 'Example' do
|
||||
attr_reader :name
|
||||
|
||||
assertion = -> {
|
||||
expect(record).to allow_value(nil).for(:name)
|
||||
}
|
||||
def name=(value)
|
||||
@name = value unless value.nil?
|
||||
end
|
||||
end
|
||||
|
||||
expect(&assertion).to raise_error(
|
||||
Shoulda::Matchers::ActiveModel::AllowValueMatcher::CouldNotSetAttributeError
|
||||
)
|
||||
record = model.new(name: 'some name')
|
||||
|
||||
assertion = lambda do
|
||||
expect(record).
|
||||
to allow_value(nil).
|
||||
for(:name).
|
||||
ignoring_interference_by_writer
|
||||
end
|
||||
|
||||
expect(&assertion).not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the attribute cannot be changed from a non-nil value to another non-nil value' do
|
||||
it 'does not raise an error at all' do
|
||||
model = define_active_model_class 'Example' do
|
||||
attr_reader :name
|
||||
|
||||
def name=(_value)
|
||||
@name = 'constant name'
|
||||
end
|
||||
end
|
||||
|
||||
record = model.new(name: 'some name')
|
||||
|
||||
assertion = lambda do
|
||||
expect(record).
|
||||
to allow_value('another name').
|
||||
for(:name).
|
||||
ignoring_interference_by_writer
|
||||
end
|
||||
|
||||
expect(&assertion).not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,28 +3,28 @@ require 'unit_spec_helper'
|
|||
describe Shoulda::Matchers::ActiveModel::ValidateAcceptanceOfMatcher, type: :model do
|
||||
context 'a model with an acceptance validation' do
|
||||
it 'accepts when the attributes match' do
|
||||
expect(validating_acceptance).to matcher
|
||||
expect(record_validating_acceptance).to matcher
|
||||
end
|
||||
|
||||
it 'does not overwrite the default message with nil' do
|
||||
expect(validating_acceptance).to matcher.with_message(nil)
|
||||
expect(record_validating_acceptance).to matcher.with_message(nil)
|
||||
end
|
||||
end
|
||||
|
||||
context 'a model without an acceptance validation' do
|
||||
it 'rejects' do
|
||||
expect(define_model(:example, attr: :string).new).not_to matcher
|
||||
expect(record_validating_nothing).not_to matcher
|
||||
end
|
||||
end
|
||||
|
||||
context 'an attribute which must be accepted with a custom message' do
|
||||
it 'accepts when the message matches' do
|
||||
expect(validating_acceptance(message: 'custom')).
|
||||
expect(record_validating_acceptance(message: 'custom')).
|
||||
to matcher.with_message(/custom/)
|
||||
end
|
||||
|
||||
it 'rejects when the message does not match' do
|
||||
expect(validating_acceptance(message: 'custom')).
|
||||
expect(record_validating_acceptance(message: 'custom')).
|
||||
not_to matcher.with_message(/wrong/)
|
||||
end
|
||||
end
|
||||
|
@ -33,9 +33,21 @@ describe Shoulda::Matchers::ActiveModel::ValidateAcceptanceOfMatcher, type: :mod
|
|||
validate_acceptance_of(:attr)
|
||||
end
|
||||
|
||||
def validating_acceptance(options = {})
|
||||
define_model(:example, attr: :string) do
|
||||
def model_validating_nothing(&block)
|
||||
define_active_model_class(:example, accessors: [:attr], &block)
|
||||
end
|
||||
|
||||
def record_validating_nothing
|
||||
model_validating_nothing.new
|
||||
end
|
||||
|
||||
def model_validating_acceptance(options = {})
|
||||
model_validating_nothing do
|
||||
validates_acceptance_of :attr, options
|
||||
end.new
|
||||
end
|
||||
end
|
||||
|
||||
def record_validating_acceptance(options = {})
|
||||
model_validating_acceptance(options).new
|
||||
end
|
||||
end
|
||||
|
|
|
@ -229,20 +229,6 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
|
|||
)
|
||||
expect(record).to validate_numericality.is_less_than_or_equal_to(18)
|
||||
end
|
||||
|
||||
context 'if the given value is right at the allowed max value for the column' do
|
||||
it 'does not raise an error' do
|
||||
record = build_record_with_integer_column_of_limit(2,
|
||||
less_than_or_equal_to: 32767
|
||||
)
|
||||
assertion = lambda do
|
||||
expect(record).
|
||||
to validate_numericality.
|
||||
is_less_than_or_equal_to(32767)
|
||||
end
|
||||
expect(&assertion).not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'and not validating with less_than_or_equal_to' do
|
||||
|
@ -268,16 +254,6 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
|
|||
to validate_numericality.
|
||||
is_less_than(18)
|
||||
end
|
||||
|
||||
context 'if the given value is right at the allowed max value for the column' do
|
||||
it 'does not raise an error' do
|
||||
record = build_record_with_integer_column_of_limit(2, less_than: 32767)
|
||||
assertion = lambda do
|
||||
expect(record).to validate_numericality.is_less_than(32767)
|
||||
end
|
||||
expect(&assertion).not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'and not validating with less_than' do
|
||||
|
@ -301,26 +277,6 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
|
|||
record = build_record_validating_numericality(equal_to: 18)
|
||||
expect(record).to validate_numericality.is_equal_to(18)
|
||||
end
|
||||
|
||||
context 'if the given value is right at the allowed min value for the column' do
|
||||
it 'does not raise an error' do
|
||||
record = build_record_with_integer_column_of_limit(2, equal_to: -32768)
|
||||
assertion = lambda do
|
||||
expect(record).to validate_numericality.is_equal_to(-32768)
|
||||
end
|
||||
expect(&assertion).not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
context 'if the given value is right at the allowed max value for the column' do
|
||||
it 'does not raise an error' do
|
||||
record = build_record_with_integer_column_of_limit(2, equal_to: 32767)
|
||||
assertion = lambda do
|
||||
expect(record).to validate_numericality.is_equal_to(32767)
|
||||
end
|
||||
expect(&assertion).not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'and not validating with equal_to' do
|
||||
|
@ -346,20 +302,6 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
|
|||
to validate_numericality.
|
||||
is_greater_than_or_equal_to(18)
|
||||
end
|
||||
|
||||
context 'if the given value is right at the allowed min value for the column' do
|
||||
it 'does not raise an error' do
|
||||
record = build_record_with_integer_column_of_limit(2,
|
||||
greater_than_or_equal_to: -32768
|
||||
)
|
||||
assertion = lambda do
|
||||
expect(record).
|
||||
to validate_numericality_of(:attr).
|
||||
is_greater_than_or_equal_to(-32768)
|
||||
end
|
||||
expect(&assertion).not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'not validating with greater_than_or_equal_to' do
|
||||
|
@ -385,20 +327,6 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
|
|||
to validate_numericality.
|
||||
is_greater_than(18)
|
||||
end
|
||||
|
||||
context 'if the given value is right at the allowed min value for the column' do
|
||||
it 'does not raise an error' do
|
||||
record = build_record_with_integer_column_of_limit(2,
|
||||
greater_than: -32768
|
||||
)
|
||||
assertion = lambda do
|
||||
expect(record).
|
||||
to validate_numericality_of(:attr).
|
||||
is_greater_than(-32768)
|
||||
end
|
||||
expect(&assertion).not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'and not validating with greater_than' do
|
||||
|
@ -511,13 +439,11 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
|
|||
only_integer.
|
||||
is_greater_than(18)
|
||||
end
|
||||
message = format_message_according_to_rails_version(
|
||||
<<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be an integer" when attr is set to 0.1,
|
||||
got errors:
|
||||
* "must be greater than 18" (attribute: attr, value: "0.1")
|
||||
MESSAGE
|
||||
)
|
||||
message = <<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be an integer" when attr is set to "0.1",
|
||||
got errors:
|
||||
* "must be greater than 18" (attribute: attr, value: "0.1")
|
||||
MESSAGE
|
||||
expect(&assertion).to fail_with_message(message)
|
||||
end
|
||||
|
||||
|
@ -529,13 +455,11 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
|
|||
only_integer.
|
||||
is_greater_than(18)
|
||||
end
|
||||
message = format_message_according_to_rails_version(
|
||||
<<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be an integer" when attr is set to 0.1,
|
||||
got errors:
|
||||
* "must be greater than 18" (attribute: attr, value: "0.1")
|
||||
MESSAGE
|
||||
)
|
||||
message = <<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be an integer" when attr is set to "0.1",
|
||||
got errors:
|
||||
* "must be greater than 18" (attribute: attr, value: "0.1")
|
||||
MESSAGE
|
||||
expect(&assertion).to fail_with_message(message)
|
||||
end
|
||||
|
||||
|
@ -550,12 +474,10 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
|
|||
even.
|
||||
is_greater_than(18)
|
||||
end
|
||||
message = format_message_according_to_rails_version(
|
||||
<<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be greater than 18" when attr is set to 18,
|
||||
got no errors
|
||||
MESSAGE
|
||||
)
|
||||
message = <<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be greater than 18" when attr is set to "18",
|
||||
got no errors
|
||||
MESSAGE
|
||||
expect(&assertion).to fail_with_message(message)
|
||||
end
|
||||
|
||||
|
@ -570,13 +492,11 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
|
|||
even.
|
||||
is_greater_than(18)
|
||||
end
|
||||
message = format_message_according_to_rails_version(
|
||||
<<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be even" when attr is set to 1,
|
||||
got errors:
|
||||
* "must be greater than 18" (attribute: attr, value: "1")
|
||||
MESSAGE
|
||||
)
|
||||
message = <<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be even" when attr is set to "1",
|
||||
got errors:
|
||||
* "must be greater than 18" (attribute: attr, value: "1")
|
||||
MESSAGE
|
||||
expect(&assertion).to fail_with_message(message)
|
||||
end
|
||||
|
||||
|
@ -591,12 +511,10 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
|
|||
odd.
|
||||
is_less_than_or_equal_to(99)
|
||||
end
|
||||
message = format_message_according_to_rails_version(
|
||||
<<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be less than or equal to 99" when attr is set to 101,
|
||||
got no errors
|
||||
MESSAGE
|
||||
)
|
||||
message = <<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be less than or equal to 99" when attr is set to "101",
|
||||
got no errors
|
||||
MESSAGE
|
||||
expect(&assertion).to fail_with_message(message)
|
||||
end
|
||||
|
||||
|
@ -613,12 +531,10 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
|
|||
is_greater_than(18).
|
||||
is_less_than(99)
|
||||
end
|
||||
message = format_message_according_to_rails_version(
|
||||
<<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be greater than 18" when attr is set to 18,
|
||||
got no errors
|
||||
MESSAGE
|
||||
)
|
||||
message = <<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be greater than 18" when attr is set to "18",
|
||||
got no errors
|
||||
MESSAGE
|
||||
expect(&assertion).to fail_with_message(message)
|
||||
end
|
||||
end
|
||||
|
@ -635,13 +551,11 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
|
|||
only_integer.
|
||||
is_greater_than(18)
|
||||
end
|
||||
message = format_message_according_to_rails_version(
|
||||
<<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be greater than 18" when attr is set to 18,
|
||||
got errors:
|
||||
* "must be greater than 19" (attribute: attr, value: "19")
|
||||
MESSAGE
|
||||
)
|
||||
message = <<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be greater than 18" when attr is set to "18",
|
||||
got errors:
|
||||
* "must be greater than 19" (attribute: attr, value: "19")
|
||||
MESSAGE
|
||||
expect(&assertion).to fail_with_message(message)
|
||||
end
|
||||
|
||||
|
@ -656,12 +570,10 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
|
|||
only_integer.
|
||||
is_greater_than(18)
|
||||
end
|
||||
message = format_message_according_to_rails_version(
|
||||
<<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be greater than 18" when attr is set to 18,
|
||||
got no errors
|
||||
MESSAGE
|
||||
)
|
||||
message = <<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be greater than 18" when attr is set to "18",
|
||||
got no errors
|
||||
MESSAGE
|
||||
expect(&assertion).to fail_with_message(message)
|
||||
end
|
||||
|
||||
|
@ -676,13 +588,11 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
|
|||
even.
|
||||
is_greater_than(18)
|
||||
end
|
||||
message = format_message_according_to_rails_version(
|
||||
<<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be greater than 18" when attr is set to 18,
|
||||
got errors:
|
||||
* "must be greater than 20" (attribute: attr, value: "20")
|
||||
MESSAGE
|
||||
)
|
||||
message = <<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be greater than 18" when attr is set to "18",
|
||||
got errors:
|
||||
* "must be greater than 20" (attribute: attr, value: "20")
|
||||
MESSAGE
|
||||
expect(&assertion).to fail_with_message(message)
|
||||
end
|
||||
|
||||
|
@ -697,12 +607,10 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
|
|||
even.
|
||||
is_greater_than(18)
|
||||
end
|
||||
message = format_message_according_to_rails_version(
|
||||
<<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be greater than 18" when attr is set to 18,
|
||||
got no errors
|
||||
MESSAGE
|
||||
)
|
||||
message = <<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be greater than 18" when attr is set to "18",
|
||||
got no errors
|
||||
MESSAGE
|
||||
expect(&assertion).to fail_with_message(message)
|
||||
end
|
||||
|
||||
|
@ -717,12 +625,10 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
|
|||
odd.
|
||||
is_less_than_or_equal_to(99)
|
||||
end
|
||||
message = format_message_according_to_rails_version(
|
||||
<<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be less than or equal to 99" when attr is set to 101,
|
||||
got no errors
|
||||
MESSAGE
|
||||
)
|
||||
message = <<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be less than or equal to 99" when attr is set to "101",
|
||||
got no errors
|
||||
MESSAGE
|
||||
expect(&assertion).to fail_with_message(message)
|
||||
end
|
||||
|
||||
|
@ -737,13 +643,11 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
|
|||
odd.
|
||||
is_less_than_or_equal_to(99)
|
||||
end
|
||||
message = format_message_according_to_rails_version(
|
||||
<<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be less than or equal to 99" when attr is set to 101,
|
||||
got errors:
|
||||
* "must be less than or equal to 97" (attribute: attr, value: "101")
|
||||
MESSAGE
|
||||
)
|
||||
message = <<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be less than or equal to 99" when attr is set to "101",
|
||||
got errors:
|
||||
* "must be less than or equal to 97" (attribute: attr, value: "101")
|
||||
MESSAGE
|
||||
expect(&assertion).to fail_with_message(message)
|
||||
end
|
||||
|
||||
|
@ -760,13 +664,11 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
|
|||
is_greater_than(18).
|
||||
is_less_than(99)
|
||||
end
|
||||
message = format_message_according_to_rails_version(
|
||||
<<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be greater than 18" when attr is set to 18,
|
||||
got errors:
|
||||
* "must be greater than 19" (attribute: attr, value: "19")
|
||||
MESSAGE
|
||||
)
|
||||
message = <<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be greater than 18" when attr is set to "18",
|
||||
got errors:
|
||||
* "must be greater than 19" (attribute: attr, value: "19")
|
||||
MESSAGE
|
||||
expect(&assertion).to fail_with_message(message)
|
||||
end
|
||||
|
||||
|
@ -783,13 +685,11 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
|
|||
is_greater_than(18).
|
||||
is_less_than(99)
|
||||
end
|
||||
message = format_message_according_to_rails_version(
|
||||
<<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be less than 99" when attr is set to 100,
|
||||
got errors:
|
||||
* "must be less than 100" (attribute: attr, value: "100")
|
||||
MESSAGE
|
||||
)
|
||||
message = <<-MESSAGE.strip_heredoc
|
||||
Expected errors to include "must be less than 99" when attr is set to "100",
|
||||
got errors:
|
||||
* "must be less than 100" (attribute: attr, value: "100")
|
||||
MESSAGE
|
||||
expect(&assertion).to fail_with_message(message)
|
||||
end
|
||||
end
|
||||
|
@ -970,23 +870,4 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
|
|||
def attribute_name
|
||||
:attr
|
||||
end
|
||||
|
||||
def define_model_with_integer_column_of_limit(limit, validation_options = {})
|
||||
column_options = { type: :integer, options: { limit: limit } }
|
||||
define_model :example, attribute_name => column_options do |model|
|
||||
model.validates_numericality_of(attribute_name, validation_options)
|
||||
end
|
||||
end
|
||||
|
||||
def build_record_with_integer_column_of_limit(limit, validation_options = {})
|
||||
define_model_with_integer_column_of_limit(limit, validation_options).new
|
||||
end
|
||||
|
||||
def format_message_according_to_rails_version(message)
|
||||
if rails_gte_4_2?
|
||||
message
|
||||
else
|
||||
message.gsub(/"((?:\d+)(?:\.\d+)?)"/, '\1')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue