thoughtbot--shoulda-matchers/spec/unit/shoulda/matchers/active_model/validate_numericality_of_ma...

1164 lines
36 KiB
Ruby
Raw Normal View History

require 'unit_spec_helper'
describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :model do
class << self
def all_qualifiers
[
{
category: :comparison,
name: :is_greater_than,
argument: 1,
validation_name: :greater_than,
validation_value: 1,
},
{
category: :comparison,
name: :is_greater_than_or_equal_to,
argument: 1,
validation_name: :greater_than_or_equal_to,
validation_value: 1,
},
{
category: :comparison,
name: :is_less_than,
argument: 1,
validation_name: :less_than,
validation_value: 1,
},
{
category: :comparison,
name: :is_less_than_or_equal_to,
argument: 1,
validation_name: :less_than_or_equal_to,
validation_value: 1,
},
{
category: :comparison,
name: :is_equal_to,
argument: 1,
validation_name: :equal_to,
validation_value: 1,
},
{
category: :cardinality,
name: :odd,
validation_name: :odd,
validation_value: true,
},
{
category: :cardinality,
name: :even,
validation_name: :even,
validation_value: true,
},
{
name: :only_integer,
validation_name: :only_integer,
validation_value: true,
},
{
name: :on,
argument: :customizable,
validation_name: :on,
validation_value: :customizable
}
]
end
def qualifiers_under(category)
all_qualifiers.select do |qualifier|
qualifier[:category] == category
end
end
def mutually_exclusive_qualifiers
qualifiers_under(:cardinality) + qualifiers_under(:comparison)
end
def non_mutually_exclusive_qualifiers
all_qualifiers - mutually_exclusive_qualifiers
end
def validations_by_qualifier
all_qualifiers.each_with_object({}) do |qualifier, hash|
hash[qualifier[:name]] = qualifier[:validation_name]
end
end
def all_qualifier_combinations
combinations = []
([nil] + mutually_exclusive_qualifiers).each do |mutually_exclusive_qualifier|
(0..non_mutually_exclusive_qualifiers.length).each do |n|
non_mutually_exclusive_qualifiers.combination(n) do |combination|
super_combination = (
[mutually_exclusive_qualifier] +
combination
)
super_combination.select!(&:present?)
if super_combination.any?
combinations << super_combination
end
end
end
end
combinations
end
def default_qualifier_arguments
all_qualifiers.each_with_object({}) do |qualifier, hash|
hash[qualifier[:name]] = qualifier[:argument]
end
end
def default_validation_values
all_qualifiers.each_with_object({}) do |qualifier, hash|
hash[qualifier[:validation_name]] = qualifier[:validation_value]
end
end
end
context 'qualified with nothing' do
context 'and validating numericality' do
it 'accepts' do
record = build_record_validating_numericality
expect(record).to validate_numericality
end
context 'when the column is an integer column' do
it 'raises an IneffectiveTestError' do
record = build_record_validating_numericality(
column_type: :integer
)
assertion = -> { expect(record).to validate_numericality }
expect(&assertion).
to raise_error(described_class::IneffectiveTestError)
end
end
context 'when the column is a float column' do
it 'raises an IneffectiveTestError' do
record = build_record_validating_numericality(
column_type: :float
)
assertion = -> { expect(record).to validate_numericality }
expect(&assertion).
to raise_error(described_class::IneffectiveTestError)
end
end
context 'when the column is a decimal column' do
it 'raises an IneffectiveTestError' do
record = build_record_validating_numericality(
column_type: :decimal,
)
assertion = -> { expect(record).to validate_numericality }
expect(&assertion).
to raise_error(described_class::IneffectiveTestError)
end
end
end
context 'and not validating anything' do
it 'rejects since it does not disallow non-numbers' do
record = build_record_validating_nothing
assertion = -> { expect(record).to validate_numericality }
expect(&assertion).to fail_with_message_including(
'Expected errors to include "is not a number"'
)
end
end
end
context 'qualified with allow_nil' do
context 'and validating with allow_nil' do
it 'accepts' do
record = build_record_validating_numericality(allow_nil: true)
expect(record).to validate_numericality.allow_nil
end
end
context 'and not validating with allow_nil' do
it 'rejects since it tries to treat nil as a number' do
record = build_record_validating_numericality
assertion = lambda do
expect(record).to validate_numericality.allow_nil
end
expect(&assertion).to fail_with_message_including(
%[Did not expect errors to include "is not a number" when #{attribute_name} is set to nil]
)
end
end
end
context 'qualified with only_integer' do
context 'and validating with only_integer' do
it 'accepts' do
record = build_record_validating_numericality(only_integer: true)
expect(record).to validate_numericality.only_integer
end
end
context 'and not validating with only_integer' do
it 'rejects since it does not disallow non-integers' do
record = build_record_validating_numericality
assertion = lambda do
expect(record).to validate_numericality.only_integer
end
expect(&assertion).to fail_with_message_including(
'Expected errors to include "must be an integer"'
)
end
end
end
context 'qualified with odd' do
context 'and validating with odd' do
it 'accepts' do
record = build_record_validating_numericality(odd: true)
expect(record).to validate_numericality.odd
end
context 'when the column is an integer column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
column_type: :integer,
odd: true
)
expect(record).to validate_numericality.odd
end
end
context 'when the column is a float column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
column_type: :float,
odd: true
)
expect(record).to validate_numericality.odd
end
end
context 'when the column is a decimal column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
column_type: :decimal,
odd: true,
)
expect(record).to validate_numericality.odd
end
end
end
context 'and not validating with odd' do
it 'rejects since it does not disallow even numbers' do
record = build_record_validating_numericality
assertion = lambda do
expect(record).to validate_numericality.odd
end
expect(&assertion).to fail_with_message_including(
'Expected errors to include "must be odd"'
)
end
end
end
context 'qualified with even' do
context 'and validating with even' do
it 'accepts' do
record = build_record_validating_numericality(even: true)
expect(record).to validate_numericality.even
end
context 'when the column is an integer column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
column_type: :integer,
even: true
)
expect(record).to validate_numericality.even
end
end
context 'when the column is a float column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
column_type: :float,
even: true
)
expect(record).to validate_numericality.even
end
end
context 'when the column is a decimal column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
column_type: :decimal,
even: true,
)
expect(record).to validate_numericality.even
end
end
end
context 'and not validating with even' do
it 'rejects since it does not disallow odd numbers' do
record = build_record_validating_numericality
assertion = -> { expect(record).to validate_numericality.even }
expect(&assertion).to fail_with_message_including(
'Expected errors to include "must be even"'
)
end
end
end
context 'qualified with is_less_than_or_equal_to' do
context 'and validating with less_than_or_equal_to' do
it 'accepts' do
record = build_record_validating_numericality(
less_than_or_equal_to: 18
)
expect(record).to validate_numericality.is_less_than_or_equal_to(18)
end
context 'when the column is an integer column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
column_type: :integer,
less_than_or_equal_to: 18
)
expect(record).to validate_numericality.is_less_than_or_equal_to(18)
end
end
context 'when the column is a float column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
column_type: :float,
less_than_or_equal_to: 18
)
expect(record).to validate_numericality.is_less_than_or_equal_to(18)
end
end
context 'when the column is a decimal column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
column_type: :decimal,
less_than_or_equal_to: 18,
)
expect(record).to validate_numericality.is_less_than_or_equal_to(18)
end
end
end
context 'and not validating with less_than_or_equal_to' do
it 'rejects since it does not disallow numbers greater than the value' do
record = build_record_validating_numericality
assertion = lambda do
expect(record).
to validate_numericality.
is_less_than_or_equal_to(18)
end
expect(&assertion).to fail_with_message_including(
'Expected errors to include "must be less than or equal to 18"'
)
end
end
end
context 'qualified with is_less_than' do
context 'and validating with less_than' do
it 'accepts' do
record = build_record_validating_numericality(less_than: 18)
expect(record).
to validate_numericality.
is_less_than(18)
end
context 'when the column is an integer column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
column_type: :integer,
less_than: 18
)
expect(record).to validate_numericality.is_less_than(18)
end
end
context 'when the column is a float column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
column_type: :float,
less_than: 18
)
expect(record).to validate_numericality.is_less_than(18)
end
end
context 'when the column is a decimal column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
column_type: :decimal,
less_than: 18,
)
expect(record).to validate_numericality.is_less_than(18)
end
end
end
context 'and not validating with less_than' do
it 'rejects since it does not disallow numbers greater than or equal to the value' do
record = build_record_validating_numericality
assertion = lambda do
expect(record).
to validate_numericality.
is_less_than(18)
end
expect(&assertion).to fail_with_message_including(
'Expected errors to include "must be less than 18"'
)
end
end
end
context 'qualified with is_equal_to' do
context 'and validating with equal_to' do
it 'accepts' do
record = build_record_validating_numericality(equal_to: 18)
expect(record).to validate_numericality.is_equal_to(18)
end
context 'when the column is an integer column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
column_type: :integer,
equal_to: 18
)
expect(record).to validate_numericality.is_equal_to(18)
end
end
context 'when the column is a float column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
column_type: :float,
equal_to: 18
)
expect(record).to validate_numericality.is_equal_to(18)
end
end
context 'when the column is a decimal column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
column_type: :decimal,
equal_to: 18,
)
expect(record).to validate_numericality.is_equal_to(18)
end
end
end
context 'and not validating with equal_to' do
it 'rejects since it does not disallow numbers that are not the value' do
record = build_record_validating_numericality
assertion = lambda do
expect(record).to validate_numericality.is_equal_to(18)
end
expect(&assertion).to fail_with_message_including(
'Expected errors to include "must be equal to 18"'
)
end
end
end
context 'qualified with is_greater_than_or_equal to' do
context 'validating with greater_than_or_equal_to' do
it 'accepts' do
record = build_record_validating_numericality(
greater_than_or_equal_to: 18
)
expect(record).
to validate_numericality.
is_greater_than_or_equal_to(18)
end
context 'when the column is an integer column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
column_type: :integer,
greater_than_or_equal_to: 18
)
expect(record).
to validate_numericality.
is_greater_than_or_equal_to(18)
end
end
context 'when the column is a float column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
column_type: :float,
greater_than_or_equal_to: 18
)
expect(record).
to validate_numericality.
is_greater_than_or_equal_to(18)
end
end
context 'when the column is a decimal column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
column_type: :decimal,
greater_than_or_equal_to: 18,
)
expect(record).
to validate_numericality.
is_greater_than_or_equal_to(18)
end
end
end
context 'not validating with greater_than_or_equal_to' do
it 'rejects since it does not disallow numbers that are less than the value' do
record = build_record_validating_numericality
assertion = lambda do
expect(record).
to validate_numericality.
is_greater_than_or_equal_to(18)
end
expect(&assertion).to fail_with_message_including(
'Expected errors to include "must be greater than or equal to 18"'
)
end
end
end
context 'qualified with is_greater_than' do
context 'and validating with greater_than' do
it 'accepts' do
record = build_record_validating_numericality(greater_than: 18)
expect(record).
to validate_numericality.
is_greater_than(18)
end
context 'when the column is an integer column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
column_type: :integer,
greater_than: 18
)
expect(record).
to validate_numericality.
is_greater_than(18)
end
end
context 'when the column is a float column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
column_type: :float,
greater_than: 18
)
expect(record).
to validate_numericality.
is_greater_than(18)
end
end
context 'when the column is a decimal column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
column_type: :decimal,
greater_than: 18,
)
expect(record).
to validate_numericality.
is_greater_than(18)
end
end
end
context 'and not validating with greater_than' do
it 'rejects since it does not disallow numbers that are less than or equal to the value' do
record = build_record_validating_numericality
assertion = lambda do
expect(record).
to validate_numericality.
is_greater_than(18)
end
expect(&assertion).to fail_with_message_including(
'Expected errors to include "must be greater than 18"'
)
end
end
end
context 'qualified with with_message' do
context 'and validating with the same message' do
it 'accepts' do
record = build_record_validating_numericality(message: 'custom')
expect(record).to validate_numericality.with_message(/custom/)
end
end
context 'and validating with a different message' do
it 'rejects' do
record = build_record_validating_numericality(message: 'custom')
expect(record).not_to validate_numericality.with_message(/wrong/)
end
end
context 'and no message is provided' do
it 'ignores the qualifier' do
record = build_record_validating_numericality
expect(record).to validate_numericality.with_message(nil)
end
end
end
context 'qualified with strict' do
context 'and validating strictly' do
it 'accepts' do
record = build_record_validating_numericality(strict: true)
expect(record).to validate_numericality.strict
end
end
context 'and not validating strictly' do
it 'rejects since ActiveModel::StrictValidationFailed is never raised' do
record = build_record_validating_numericality(attribute_name: :attr)
assertion = lambda do
expect(record).to validate_numericality_of(:attr).strict
end
expect(&assertion).to fail_with_message_including(
'Expected exception to include "Attr is not a number"'
)
end
end
end
context 'qualified with on and validating with on' do
it 'accepts' do
record = build_record_validating_numericality(on: :customizable)
expect(record).to validate_numericality.on(:customizable)
end
end
context 'qualified with on but not validating with on' do
it 'accepts since the validation never considers a context' do
record = build_record_validating_numericality
expect(record).to validate_numericality.on(:customizable)
end
end
context 'not qualified with on but validating with on' do
it 'rejects since the validation never runs' do
record = build_record_validating_numericality(on: :customizable)
assertion = lambda do
expect(record).to validate_numericality
end
expect(&assertion).to fail_with_message_including(
'Expected errors to include "is not a number"'
)
end
end
context 'with combinations of qualifiers together' do
all_qualifier_combinations.each do |combination|
if combination.size > 1
it do
validation_options = build_validation_options(for: combination)
record = build_record_validating_numericality(validation_options)
validate_numericality = self.validate_numericality
apply_qualifiers!(for: combination, to: validate_numericality)
expect(record).to validate_numericality
end
end
end
context 'when the qualifiers do not match the validation options' do
specify 'such as validating even but testing that only_integer is validated' do
record = build_record_validating_numericality(
even: true,
greater_than: 18
)
assertion = lambda do
expect(record).
to validate_numericality.
only_integer.
is_greater_than(18)
end
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
2015-09-26 05:10:00 +00:00
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
specify 'such as not validating only_integer but testing that only_integer is validated' do
record = build_record_validating_numericality(greater_than: 18)
assertion = lambda do
expect(record).
to validate_numericality.
only_integer.
is_greater_than(18)
end
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
2015-09-26 05:10:00 +00:00
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
specify 'such as validating greater_than_or_equal_to (+ even) but testing that greater_than is validated' do
record = build_record_validating_numericality(
even: true,
greater_than_or_equal_to: 18
)
assertion = lambda do
expect(record).
to validate_numericality.
even.
is_greater_than(18)
end
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
2015-09-26 05:10:00 +00:00
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
specify 'such as validating odd (+ greater_than) but testing that even is validated' do
record = build_record_validating_numericality(
odd: true,
greater_than: 18
)
assertion = lambda do
expect(record).
to validate_numericality.
even.
is_greater_than(18)
end
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
2015-09-26 05:10:00 +00:00
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
specify 'such as validating greater_than_or_equal_to (+ odd) but testing that is_less_than_or_equal_to is validated' do
record = build_record_validating_numericality(
odd: true,
greater_than_or_equal_to: 99
)
assertion = lambda do
expect(record).
to validate_numericality.
odd.
is_less_than_or_equal_to(99)
end
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
2015-09-26 05:10:00 +00:00
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
specify 'such as validating greater_than_or_equal_to (+ only_integer + less_than) but testing that greater_than is validated' do
record = build_record_validating_numericality(
only_integer: true,
greater_than_or_equal_to: 18,
less_than: 99
)
assertion = lambda do
expect(record).
to validate_numericality.
only_integer.
is_greater_than(18).
is_less_than(99)
end
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
2015-09-26 05:10:00 +00:00
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
context 'when qualifiers match the validation options but the values are different' do
specify 'such as testing greater_than (+ only_integer) with lower value' do
record = build_record_validating_numericality(
only_integer: true,
greater_than: 19
)
assertion = lambda do
expect(record).
to validate_numericality.
only_integer.
is_greater_than(18)
end
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
2015-09-26 05:10:00 +00:00
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
specify 'such as testing greater_than (+ only_integer) with higher value' do
record = build_record_validating_numericality(
only_integer: true,
greater_than: 17
)
assertion = lambda do
expect(record).
to validate_numericality.
only_integer.
is_greater_than(18)
end
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
2015-09-26 05:10:00 +00:00
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
specify 'such as testing greater_than (+ even) with lower value' do
record = build_record_validating_numericality(
even: true,
greater_than: 20
)
assertion = lambda do
expect(record).
to validate_numericality.
even.
is_greater_than(18)
end
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
2015-09-26 05:10:00 +00:00
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
specify 'such as testing greater than (+ even) with higher value' do
record = build_record_validating_numericality(
even: true,
greater_than: 16
)
assertion = lambda do
expect(record).
to validate_numericality.
even.
is_greater_than(18)
end
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
2015-09-26 05:10:00 +00:00
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
specify 'such as testing less_than_or_equal_to (+ odd) with lower value' do
record = build_record_validating_numericality(
odd: true,
less_than_or_equal_to: 101
)
assertion = lambda do
expect(record).
to validate_numericality.
odd.
is_less_than_or_equal_to(99)
end
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
2015-09-26 05:10:00 +00:00
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
specify 'such as testing less_than_or_equal_to (+ odd) with higher value' do
record = build_record_validating_numericality(
odd: true,
less_than_or_equal_to: 97
)
assertion = lambda do
expect(record).
to validate_numericality.
odd.
is_less_than_or_equal_to(99)
end
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
2015-09-26 05:10:00 +00:00
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
specify 'such as testing greater_than (+ only_integer + less_than) with lower value' do
record = build_record_validating_numericality(
only_integer: true,
greater_than: 19,
less_than: 99
)
assertion = lambda do
expect(record).
to validate_numericality.
only_integer.
is_greater_than(18).
is_less_than(99)
end
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
2015-09-26 05:10:00 +00:00
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
specify 'such as testing less_than (+ only_integer + greater_than) with higher value' do
record = build_record_validating_numericality(
only_integer: true,
greater_than: 18,
less_than: 100
)
assertion = lambda do
expect(record).
to validate_numericality.
only_integer.
is_greater_than(18).
is_less_than(99)
end
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
2015-09-26 05:10:00 +00:00
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
end
context 'with large numbers' do
it do
record = build_record_validating_numericality(greater_than: 100_000)
expect(record).to validate_numericality.is_greater_than(100_000)
end
it do
record = build_record_validating_numericality(less_than: 100_000)
expect(record).to validate_numericality.is_less_than(100_000)
end
it do
record = build_record_validating_numericality(
greater_than_or_equal_to: 100_000
)
expect(record).
to validate_numericality.
is_greater_than_or_equal_to(100_000)
end
it do
record = build_record_validating_numericality(
less_than_or_equal_to: 100_000
)
expect(record).
to validate_numericality.
is_less_than_or_equal_to(100_000)
end
2012-12-20 05:04:27 +00:00
end
context 'when the subject is stubbed' do
it 'retains that stub while the validate_numericality is matching' do
model = define_model :example, attr: :string do
2014-01-17 20:20:44 +00:00
validates_numericality_of :attr, odd: true
before_validation :set_attr!
def set_attr!; self.attr = 5 end
end
record = model.new
allow(record).to receive(:set_attr!)
expect(record).to validate_numericality_of(:attr).odd
end
end
context 'against an ActiveModel model' do
it 'accepts' do
model = define_active_model_class :example, accessors: [:attr] do
validates_numericality_of :attr
end
expect(model.new).to validate_numericality_of(:attr)
end
end
describe '#description' do
context 'qualified with nothing' do
it 'describes that it allows numbers' do
matcher = validate_numericality_of(:attr)
expect(matcher.description).to eq 'only allow numbers for attr'
end
end
context 'qualified with only_integer' do
it 'describes that it allows integers' do
matcher = validate_numericality_of(:attr).only_integer
expect(matcher.description).to eq 'only allow integers for attr'
end
end
qualifiers_under(:cardinality).each do |qualifier|
context "qualified with #{qualifier[:name]}" do
it "describes that it allows #{qualifier[:name]} numbers" do
matcher = validate_numericality_of(:attr).__send__(qualifier[:name])
expect(matcher.description).
to eq "only allow #{qualifier[:name]} numbers for attr"
end
end
end
qualifiers_under(:comparison).each do |qualifier|
comparison_phrase = qualifier[:validation_name].to_s.gsub('_', ' ')
context "qualified with #{qualifier[:name]}" do
it "describes that it allows numbers #{comparison_phrase} a certain value" do
matcher = validate_numericality_of(:attr).
__send__(qualifier[:name], 18)
expect(matcher.description).to eq(
"only allow numbers for attr which are #{comparison_phrase} 18"
)
end
end
end
context 'qualified with odd + is_greater_than_or_equal_to' do
it "describes that it allows odd numbers greater than or equal to a certain value" do
matcher = validate_numericality_of(:attr).
odd.
is_greater_than_or_equal_to(18)
expect(matcher.description).to eq(
'only allow odd numbers for attr which are greater than or equal to 18'
)
end
end
context 'qualified with only integer + is_greater_than + less_than_or_equal_to' do
it 'describes that it allows integer greater than one value and less than or equal to another' do
matcher = validate_numericality_of(:attr).
only_integer.
is_greater_than(18).
is_less_than_or_equal_to(100)
expect(matcher.description).to eq(
'only allow integers for attr which are greater than 18 and less than or equal to 100'
)
end
end
context 'qualified with strict' do
it 'describes that it relies upon a strict validation' do
matcher = validate_numericality_of(:attr).strict
expect(matcher.description).to eq(
'only allow numbers for attr, strictly'
)
end
context 'and qualified with a comparison qualifier' do
it 'places the comparison description after "strictly"' do
matcher = validate_numericality_of(:attr).is_less_than(18).strict
expect(matcher.description).to eq(
'only allow numbers for attr, strictly, which are less than 18'
)
end
end
end
end
def build_validation_options(args)
combination = args.fetch(:for)
combination.each_with_object({}) do |qualifier, hash|
value = self.class.default_validation_values.fetch(qualifier[:validation_name])
hash[qualifier[:validation_name]] = value
end
end
def apply_qualifiers!(args)
combination = args.fetch(:for)
matcher = args.fetch(:to)
combination.each do |qualifier|
args = self.class.default_qualifier_arguments.fetch(qualifier[:name])
matcher.__send__(qualifier[:name], *args)
end
end
def define_model_validating_numericality(options = {})
attribute_name = options.delete(:attribute_name) { self.attribute_name }
column_type = options.delete(:column_type) { :string }
define_model 'Example', attribute_name => { type: column_type } do |model|
model.validates_numericality_of(attribute_name, options)
end
end
def build_record_validating_numericality(options = {})
define_model_validating_numericality(options).new
end
def define_model_validating_nothing
define_model('Example', attribute_name => :string)
end
def build_record_validating_nothing
define_model_validating_nothing.new
end
def validate_numericality
validate_numericality_of(attribute_name)
end
def attribute_name
:attr
end
end