2014-10-27 00:51:42 +00:00
|
|
|
|
require 'unit_spec_helper'
|
2010-12-13 22:28:59 +00:00
|
|
|
|
|
2014-12-24 21:37:04 +00:00
|
|
|
|
describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :model do
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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,
|
2015-04-01 06:00:14 +00:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: :on,
|
|
|
|
|
argument: :customizable,
|
|
|
|
|
validation_name: :on,
|
|
|
|
|
validation_value: :customizable
|
2015-03-29 00:12:24 +00:00
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def qualifiers_under(category)
|
|
|
|
|
all_qualifiers.select do |qualifier|
|
|
|
|
|
qualifier[:category] == category
|
|
|
|
|
end
|
2012-10-16 17:45:06 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
def mutually_exclusive_qualifiers
|
|
|
|
|
qualifiers_under(:cardinality) + qualifiers_under(:comparison)
|
2010-12-13 22:28:59 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
def non_mutually_exclusive_qualifiers
|
|
|
|
|
all_qualifiers - mutually_exclusive_qualifiers
|
2011-10-27 02:21:06 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
def validations_by_qualifier
|
|
|
|
|
all_qualifiers.each_with_object({}) do |qualifier, hash|
|
|
|
|
|
hash[qualifier[:name]] = qualifier[:validation_name]
|
|
|
|
|
end
|
2013-04-12 17:11:07 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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
|
2011-10-27 02:21:06 +00:00
|
|
|
|
end
|
2013-03-19 04:50:39 +00:00
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
def default_qualifier_arguments
|
|
|
|
|
all_qualifiers.each_with_object({}) do |qualifier, hash|
|
|
|
|
|
hash[qualifier[:name]] = qualifier[:argument]
|
|
|
|
|
end
|
2013-03-19 04:50:39 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
def default_validation_values
|
|
|
|
|
all_qualifiers.each_with_object({}) do |qualifier, hash|
|
|
|
|
|
hash[qualifier[:validation_name]] = qualifier[:validation_value]
|
|
|
|
|
end
|
2013-03-19 04:50:39 +00:00
|
|
|
|
end
|
2011-10-27 02:21:06 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
context 'qualified with nothing' do
|
|
|
|
|
context 'and validating numericality' do
|
|
|
|
|
it 'accepts' do
|
|
|
|
|
record = build_record_validating_numericality
|
|
|
|
|
expect(record).to validate_numericality
|
|
|
|
|
end
|
2015-10-07 05:12:30 +00:00
|
|
|
|
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-31 01:47:46 +00:00
|
|
|
|
it_supports(
|
|
|
|
|
'ignoring_interference_by_writer',
|
|
|
|
|
tests: {
|
|
|
|
|
accept_if_qualified_but_changing_value_does_not_interfere: {
|
|
|
|
|
changing_values_with: :next_value,
|
|
|
|
|
},
|
|
|
|
|
reject_if_qualified_but_changing_value_interferes: {
|
|
|
|
|
model_name: 'Example',
|
|
|
|
|
attribute_name: :attr,
|
|
|
|
|
changing_values_with: :numeric_value,
|
|
|
|
|
expected_message: <<-MESSAGE.strip
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like a number, but this
|
|
|
|
|
could not be proved.
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-31 01:47:46 +00:00
|
|
|
|
After setting :attr to ‹"abcd"› -- which was read back as ‹"1"› -- the
|
|
|
|
|
matcher expected the Example to be invalid, but it was valid instead.
|
|
|
|
|
|
|
|
|
|
As indicated in the message above, :attr seems to be changing certain
|
|
|
|
|
values as they are set, and this could have something to do with why
|
|
|
|
|
this test is failing. If you've overridden the writer method for this
|
|
|
|
|
attribute, then you may need to change it to make this test pass, or
|
|
|
|
|
do something else entirely.
|
|
|
|
|
MESSAGE
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
2015-10-28 17:01:26 +00:00
|
|
|
|
context 'when the attribute is a virtual attribute in an ActiveRecord model' do
|
|
|
|
|
it 'accepts' do
|
|
|
|
|
record = build_record_validating_numericality_of_virtual_attribute
|
|
|
|
|
expect(record).to validate_numericality
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-10-07 05:12:30 +00:00
|
|
|
|
context 'when the column is an integer column' do
|
2016-01-06 04:30:21 +00:00
|
|
|
|
it 'accepts (and does not raise an AttributeChangedValueError)' do
|
|
|
|
|
record = build_record_validating_numericality(column_type: :integer)
|
|
|
|
|
expect(record).to validate_numericality
|
2015-10-07 05:12:30 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when the column is a float column' do
|
2016-01-06 04:30:21 +00:00
|
|
|
|
it 'accepts (and does not raise an AttributeChangedValueError)' do
|
|
|
|
|
record = build_record_validating_numericality(column_type: :float)
|
|
|
|
|
expect(record).to validate_numericality
|
2015-10-07 05:12:30 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
2015-10-21 20:39:39 +00:00
|
|
|
|
|
|
|
|
|
context 'when the column is a decimal column' do
|
2016-01-06 04:30:21 +00:00
|
|
|
|
it 'accepts (and does not raise an AttributeChangedValueError)' do
|
|
|
|
|
record = build_record_validating_numericality(column_type: :decimal)
|
|
|
|
|
expect(record).to validate_numericality
|
2015-10-21 20:39:39 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
2016-01-10 06:15:03 +00:00
|
|
|
|
|
|
|
|
|
if database_supports_money_columns?
|
|
|
|
|
context 'when the column is a money column' do
|
|
|
|
|
it 'accepts (and does not raise an AttributeChangedValueError)' do
|
|
|
|
|
record = build_record_validating_numericality(column_type: :money)
|
|
|
|
|
expect(record).to validate_numericality
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
|
|
|
|
|
context 'when used in the negative' do
|
|
|
|
|
it 'fails' do
|
|
|
|
|
assertion = lambda do
|
|
|
|
|
record = build_record_validating_numericality
|
|
|
|
|
expect(record).not_to validate_numericality
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
|
|
|
|
Expected Example not to validate that :attr looks like a number, but
|
|
|
|
|
this could not be proved.
|
|
|
|
|
After setting :attr to ‹"abcd"›, the matcher expected the Example to
|
|
|
|
|
be valid, but it was invalid instead, producing these validation
|
|
|
|
|
errors:
|
|
|
|
|
|
|
|
|
|
* attr: ["is not a number"]
|
|
|
|
|
MESSAGE
|
|
|
|
|
|
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
|
|
|
|
end
|
|
|
|
|
end
|
2014-02-21 22:31:48 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
context 'and not validating anything' do
|
|
|
|
|
it 'rejects since it does not disallow non-numbers' do
|
|
|
|
|
record = build_record_validating_nothing
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
assertion = -> { expect(record).to validate_numericality }
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like a number, but this
|
|
|
|
|
could not be proved.
|
2015-12-22 07:00:51 +00:00
|
|
|
|
After setting :attr to ‹"abcd"›, the matcher expected the Example to
|
|
|
|
|
be invalid, but it was valid instead.
|
2015-12-13 23:45:24 +00:00
|
|
|
|
MESSAGE
|
|
|
|
|
|
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2015-03-29 00:12:24 +00:00
|
|
|
|
end
|
2014-02-21 22:31:48 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-31 01:47:46 +00:00
|
|
|
|
|
|
|
|
|
it_supports(
|
|
|
|
|
'ignoring_interference_by_writer',
|
|
|
|
|
tests: {
|
|
|
|
|
accept_if_qualified_but_changing_value_does_not_interfere: {
|
|
|
|
|
changing_values_with: :next_value_or_numeric_value,
|
|
|
|
|
},
|
|
|
|
|
reject_if_qualified_but_changing_value_interferes: {
|
|
|
|
|
model_name: 'Example',
|
|
|
|
|
attribute_name: :attr,
|
|
|
|
|
changing_values_with: :next_value_or_non_numeric_value,
|
|
|
|
|
expected_message: <<-MESSAGE.strip
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like a number as long as
|
|
|
|
|
it is not nil, but this could not be proved.
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-31 01:47:46 +00:00
|
|
|
|
In checking that Example allows :attr to be ‹nil›, after setting :attr
|
|
|
|
|
to ‹nil› -- which was read back as ‹"a"› -- the matcher expected the
|
|
|
|
|
Example to be valid, but it was invalid instead, producing these
|
|
|
|
|
validation errors:
|
|
|
|
|
|
|
|
|
|
* attr: ["is not a number"]
|
|
|
|
|
|
|
|
|
|
As indicated in the message above, :attr seems to be changing certain
|
|
|
|
|
values as they are set, and this could have something to do with why
|
|
|
|
|
this test is failing. If you've overridden the writer method for this
|
|
|
|
|
attribute, then you may need to change it to make this test pass, or
|
|
|
|
|
do something else entirely.
|
|
|
|
|
MESSAGE
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
) do
|
|
|
|
|
def validation_matcher_scenario_args
|
|
|
|
|
super.deep_merge(validation_options: { allow_nil: true })
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def configure_validation_matcher(matcher)
|
|
|
|
|
matcher.allow_nil
|
|
|
|
|
end
|
|
|
|
|
end
|
2010-12-13 22:28:59 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
assertion = lambda do
|
|
|
|
|
expect(record).to validate_numericality.allow_nil
|
|
|
|
|
end
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like a number as long as
|
|
|
|
|
it is not nil, but this could not be proved.
|
2015-12-22 07:00:51 +00:00
|
|
|
|
In checking that Example allows :attr to be ‹nil›, after setting :attr
|
|
|
|
|
to ‹nil›, the matcher expected the Example to be valid, but it was
|
2015-12-13 23:45:24 +00:00
|
|
|
|
invalid instead, producing these validation errors:
|
|
|
|
|
|
|
|
|
|
* attr: ["is not a number"]
|
|
|
|
|
MESSAGE
|
|
|
|
|
|
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2015-03-29 00:12:24 +00:00
|
|
|
|
end
|
2010-12-13 22:28:59 +00:00
|
|
|
|
end
|
2015-03-29 00:12:24 +00:00
|
|
|
|
end
|
2010-12-13 22:28:59 +00:00
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-31 01:47:46 +00:00
|
|
|
|
|
|
|
|
|
it_supports(
|
|
|
|
|
'ignoring_interference_by_writer',
|
|
|
|
|
tests: {
|
|
|
|
|
accept_if_qualified_but_changing_value_does_not_interfere: {
|
|
|
|
|
changing_values_with: :next_value,
|
|
|
|
|
},
|
|
|
|
|
reject_if_qualified_but_changing_value_interferes: {
|
|
|
|
|
model_name: 'Example',
|
|
|
|
|
attribute_name: :attr,
|
|
|
|
|
changing_values_with: :numeric_value,
|
|
|
|
|
expected_message: <<-MESSAGE.strip
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like an integer, but this
|
|
|
|
|
could not be proved.
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-31 01:47:46 +00:00
|
|
|
|
After setting :attr to ‹"0.1"› -- which was read back as ‹"1"› -- the
|
|
|
|
|
matcher expected the Example to be invalid, but it was valid instead.
|
|
|
|
|
|
|
|
|
|
As indicated in the message above, :attr seems to be changing certain
|
|
|
|
|
values as they are set, and this could have something to do with why
|
|
|
|
|
this test is failing. If you've overridden the writer method for this
|
|
|
|
|
attribute, then you may need to change it to make this test pass, or
|
|
|
|
|
do something else entirely.
|
|
|
|
|
MESSAGE
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
) do
|
|
|
|
|
def validation_matcher_scenario_args
|
|
|
|
|
super.deep_merge(validation_options: { only_integer: true })
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def configure_validation_matcher(matcher)
|
|
|
|
|
matcher.only_integer
|
|
|
|
|
end
|
|
|
|
|
end
|
2010-12-13 22:28:59 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
context 'and not validating with only_integer' do
|
|
|
|
|
it 'rejects since it does not disallow non-integers' do
|
|
|
|
|
record = build_record_validating_numericality
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
assertion = lambda do
|
|
|
|
|
expect(record).to validate_numericality.only_integer
|
|
|
|
|
end
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like an integer, but this
|
|
|
|
|
could not be proved.
|
2015-12-22 07:00:51 +00:00
|
|
|
|
After setting :attr to ‹"0.1"›, the matcher expected the Example to be
|
2015-12-13 23:45:24 +00:00
|
|
|
|
invalid, but it was valid instead.
|
|
|
|
|
MESSAGE
|
|
|
|
|
|
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2015-03-29 00:12:24 +00:00
|
|
|
|
end
|
2013-03-19 04:50:39 +00:00
|
|
|
|
end
|
2015-03-29 00:12:24 +00:00
|
|
|
|
end
|
2013-03-19 04:50:39 +00:00
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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
|
2015-10-07 05:12:30 +00:00
|
|
|
|
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-31 01:47:46 +00:00
|
|
|
|
it_supports(
|
|
|
|
|
'ignoring_interference_by_writer',
|
|
|
|
|
tests: {
|
|
|
|
|
accept_if_qualified_but_changing_value_does_not_interfere: {
|
|
|
|
|
changing_values_with: :next_next_value,
|
|
|
|
|
},
|
|
|
|
|
reject_if_qualified_but_changing_value_interferes: {
|
|
|
|
|
model_name: 'Example',
|
|
|
|
|
attribute_name: :attr,
|
|
|
|
|
changing_values_with: :next_value,
|
|
|
|
|
expected_message: <<-MESSAGE.strip
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like an odd number, but
|
|
|
|
|
this could not be proved.
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-31 01:47:46 +00:00
|
|
|
|
After setting :attr to ‹"2"› -- which was read back as ‹"3"› -- the
|
|
|
|
|
matcher expected the Example to be invalid, but it was valid instead.
|
|
|
|
|
|
|
|
|
|
As indicated in the message above, :attr seems to be changing certain
|
|
|
|
|
values as they are set, and this could have something to do with why
|
|
|
|
|
this test is failing. If you've overridden the writer method for this
|
|
|
|
|
attribute, then you may need to change it to make this test pass, or
|
|
|
|
|
do something else entirely.
|
|
|
|
|
MESSAGE
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
) do
|
|
|
|
|
def validation_matcher_scenario_args
|
|
|
|
|
super.deep_merge(validation_options: { odd: true })
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def configure_validation_matcher(matcher)
|
|
|
|
|
matcher.odd
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-10-28 17:01:26 +00:00
|
|
|
|
context 'when the attribute is a virtual attribute in ActiveRecord model' do
|
|
|
|
|
it 'accepts' do
|
|
|
|
|
record = build_record_validating_numericality_of_virtual_attribute(
|
|
|
|
|
odd: true
|
|
|
|
|
)
|
|
|
|
|
expect(record).to validate_numericality.odd
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-10-07 05:12:30 +00:00
|
|
|
|
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
|
2015-10-21 20:39:39 +00:00
|
|
|
|
|
|
|
|
|
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
|
2013-03-19 04:50:39 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
context 'and not validating with odd' do
|
|
|
|
|
it 'rejects since it does not disallow even numbers' do
|
|
|
|
|
record = build_record_validating_numericality
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
assertion = lambda do
|
|
|
|
|
expect(record).to validate_numericality.odd
|
|
|
|
|
end
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like an odd number, but
|
|
|
|
|
this could not be proved.
|
2015-12-22 07:00:51 +00:00
|
|
|
|
After setting :attr to ‹"2"›, the matcher expected the Example to be
|
2015-12-13 23:45:24 +00:00
|
|
|
|
invalid, but it was valid instead.
|
|
|
|
|
MESSAGE
|
|
|
|
|
|
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2015-03-29 00:12:24 +00:00
|
|
|
|
end
|
2013-03-19 04:50:39 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
context 'qualified with even' do
|
|
|
|
|
context 'and validating with even' do
|
2015-10-07 05:12:30 +00:00
|
|
|
|
it 'accepts' do
|
2015-03-29 00:12:24 +00:00
|
|
|
|
record = build_record_validating_numericality(even: true)
|
|
|
|
|
expect(record).to validate_numericality.even
|
|
|
|
|
end
|
2015-10-07 05:12:30 +00:00
|
|
|
|
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-31 01:47:46 +00:00
|
|
|
|
it_supports(
|
|
|
|
|
'ignoring_interference_by_writer',
|
|
|
|
|
tests: {
|
|
|
|
|
accept_if_qualified_but_changing_value_does_not_interfere: {
|
|
|
|
|
changing_values_with: :next_next_value,
|
|
|
|
|
},
|
|
|
|
|
reject_if_qualified_but_changing_value_interferes: {
|
|
|
|
|
model_name: 'Example',
|
|
|
|
|
attribute_name: :attr,
|
|
|
|
|
changing_values_with: :next_value,
|
|
|
|
|
expected_message: <<-MESSAGE.strip
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like an even number, but
|
|
|
|
|
this could not be proved.
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-31 01:47:46 +00:00
|
|
|
|
After setting :attr to ‹"1"› -- which was read back as ‹"2"› -- the
|
|
|
|
|
matcher expected the Example to be invalid, but it was valid instead.
|
|
|
|
|
|
|
|
|
|
As indicated in the message above, :attr seems to be changing certain
|
|
|
|
|
values as they are set, and this could have something to do with why
|
|
|
|
|
this test is failing. If you've overridden the writer method for this
|
|
|
|
|
attribute, then you may need to change it to make this test pass, or
|
|
|
|
|
do something else entirely.
|
|
|
|
|
MESSAGE
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
) do
|
|
|
|
|
def validation_matcher_scenario_args
|
|
|
|
|
super.deep_merge(validation_options: { even: true })
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def configure_validation_matcher(matcher)
|
|
|
|
|
matcher.even
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-10-28 17:01:26 +00:00
|
|
|
|
context 'when the attribute is a virtual attribute in an ActiveRecord model' do
|
|
|
|
|
it 'accepts' do
|
|
|
|
|
record = build_record_validating_numericality_of_virtual_attribute(
|
|
|
|
|
even: true,
|
|
|
|
|
)
|
|
|
|
|
expect(record).to validate_numericality.even
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-10-07 05:12:30 +00:00
|
|
|
|
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
|
2015-10-21 20:39:39 +00:00
|
|
|
|
|
|
|
|
|
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
|
2013-03-19 04:50:39 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
context 'and not validating with even' do
|
|
|
|
|
it 'rejects since it does not disallow odd numbers' do
|
|
|
|
|
record = build_record_validating_numericality
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
assertion = lambda do
|
|
|
|
|
expect(record).to validate_numericality.even
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like an even number, but
|
|
|
|
|
this could not be proved.
|
2015-12-22 07:00:51 +00:00
|
|
|
|
After setting :attr to ‹"1"›, the matcher expected the Example to be
|
2015-12-13 23:45:24 +00:00
|
|
|
|
invalid, but it was valid instead.
|
|
|
|
|
MESSAGE
|
|
|
|
|
|
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2015-03-29 00:12:24 +00:00
|
|
|
|
end
|
2013-03-19 04:50:39 +00:00
|
|
|
|
end
|
2015-03-29 00:12:24 +00:00
|
|
|
|
end
|
2013-03-19 04:50:39 +00:00
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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
|
2015-10-07 05:12:30 +00:00
|
|
|
|
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-31 01:47:46 +00:00
|
|
|
|
it_supports(
|
|
|
|
|
'ignoring_interference_by_writer',
|
|
|
|
|
tests: {
|
|
|
|
|
reject_if_qualified_but_changing_value_interferes: {
|
|
|
|
|
model_name: 'Example',
|
|
|
|
|
attribute_name: :attr,
|
|
|
|
|
changing_values_with: :next_value,
|
|
|
|
|
expected_message: <<-MESSAGE.strip
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like a number less than or
|
|
|
|
|
equal to 18, but this could not be proved.
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-31 01:47:46 +00:00
|
|
|
|
After setting :attr to ‹"18"› -- which was read back as ‹"19"› -- the
|
|
|
|
|
matcher expected the Example to be valid, but it was invalid instead,
|
|
|
|
|
producing these validation errors:
|
|
|
|
|
|
|
|
|
|
* attr: ["must be less than or equal to 18"]
|
|
|
|
|
|
|
|
|
|
As indicated in the message above, :attr seems to be changing certain
|
|
|
|
|
values as they are set, and this could have something to do with why
|
|
|
|
|
this test is failing. If you've overridden the writer method for this
|
|
|
|
|
attribute, then you may need to change it to make this test pass, or
|
|
|
|
|
do something else entirely.
|
|
|
|
|
MESSAGE
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
) do
|
|
|
|
|
def validation_matcher_scenario_args
|
|
|
|
|
super.deep_merge(
|
|
|
|
|
validation_options: { less_than_or_equal_to: 18 }
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def configure_validation_matcher(matcher)
|
|
|
|
|
matcher.is_less_than_or_equal_to(18)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-10-28 17:01:26 +00:00
|
|
|
|
context 'when the attribute is a virtual attribute in an ActiveRecord model' do
|
|
|
|
|
it 'accepts' do
|
|
|
|
|
record = build_record_validating_numericality_of_virtual_attribute(
|
|
|
|
|
less_than_or_equal_to: 18,
|
|
|
|
|
)
|
|
|
|
|
expect(record).to validate_numericality.is_less_than_or_equal_to(18)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-10-07 05:12:30 +00:00
|
|
|
|
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
|
2015-10-21 20:39:39 +00:00
|
|
|
|
|
|
|
|
|
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
|
2014-02-27 03:26:37 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
assertion = lambda do
|
2015-12-13 23:45:24 +00:00
|
|
|
|
expect(record).to validate_numericality.is_less_than_or_equal_to(18)
|
2015-03-29 00:12:24 +00:00
|
|
|
|
end
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like a number less than or
|
|
|
|
|
equal to 18, but this could not be proved.
|
2015-12-22 07:00:51 +00:00
|
|
|
|
After setting :attr to ‹"19"›, the matcher expected the Example to be
|
2015-12-13 23:45:24 +00:00
|
|
|
|
invalid, but it was valid instead.
|
|
|
|
|
MESSAGE
|
|
|
|
|
|
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2015-03-29 00:12:24 +00:00
|
|
|
|
end
|
2015-01-21 22:46:26 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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
|
2015-10-07 05:12:30 +00:00
|
|
|
|
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-31 01:47:46 +00:00
|
|
|
|
it_supports(
|
|
|
|
|
'ignoring_interference_by_writer',
|
|
|
|
|
tests: {
|
|
|
|
|
reject_if_qualified_but_changing_value_interferes: {
|
|
|
|
|
model_name: 'Example',
|
|
|
|
|
attribute_name: :attr,
|
|
|
|
|
changing_values_with: :next_value,
|
|
|
|
|
expected_message: <<-MESSAGE.strip
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like a number less than
|
|
|
|
|
18, but this could not be proved.
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-31 01:47:46 +00:00
|
|
|
|
After setting :attr to ‹"17"› -- which was read back as ‹"18"› -- the
|
|
|
|
|
matcher expected the Example to be valid, but it was invalid instead,
|
|
|
|
|
producing these validation errors:
|
|
|
|
|
|
|
|
|
|
* attr: ["must be less than 18"]
|
|
|
|
|
|
|
|
|
|
As indicated in the message above, :attr seems to be changing certain
|
|
|
|
|
values as they are set, and this could have something to do with why
|
|
|
|
|
this test is failing. If you've overridden the writer method for this
|
|
|
|
|
attribute, then you may need to change it to make this test pass, or
|
|
|
|
|
do something else entirely.
|
|
|
|
|
MESSAGE
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
) do
|
|
|
|
|
def validation_matcher_scenario_args
|
|
|
|
|
super.deep_merge(validation_options: { less_than: 18 })
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def configure_validation_matcher(matcher)
|
|
|
|
|
matcher.is_less_than(18)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-10-28 17:01:26 +00:00
|
|
|
|
context 'when the attribute is a virtual attribute in an ActiveRecord model' do
|
|
|
|
|
it 'accepts' do
|
|
|
|
|
record = build_record_validating_numericality_of_virtual_attribute(
|
|
|
|
|
less_than: 18,
|
|
|
|
|
)
|
|
|
|
|
expect(record).to validate_numericality.is_less_than(18)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-10-07 05:12:30 +00:00
|
|
|
|
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
|
2015-10-21 20:39:39 +00:00
|
|
|
|
|
|
|
|
|
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
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
assertion = lambda do
|
2015-12-13 23:45:24 +00:00
|
|
|
|
expect(record).to validate_numericality.is_less_than(18)
|
2015-03-29 00:12:24 +00:00
|
|
|
|
end
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like a number less than
|
|
|
|
|
18, but this could not be proved.
|
2015-12-22 07:00:51 +00:00
|
|
|
|
After setting :attr to ‹"19"›, the matcher expected the Example to be
|
2015-12-13 23:45:24 +00:00
|
|
|
|
invalid, but it was valid instead.
|
|
|
|
|
MESSAGE
|
|
|
|
|
|
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2015-03-29 00:12:24 +00:00
|
|
|
|
end
|
2015-01-21 22:46:26 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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
|
2015-10-07 05:12:30 +00:00
|
|
|
|
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-31 01:47:46 +00:00
|
|
|
|
it_supports(
|
|
|
|
|
'ignoring_interference_by_writer',
|
|
|
|
|
tests: {
|
|
|
|
|
reject_if_qualified_but_changing_value_interferes: {
|
|
|
|
|
model_name: 'Example',
|
|
|
|
|
attribute_name: :attr,
|
|
|
|
|
changing_values_with: :next_value,
|
|
|
|
|
expected_message: <<-MESSAGE.strip
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like a number equal to 18,
|
|
|
|
|
but this could not be proved.
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-31 01:47:46 +00:00
|
|
|
|
After setting :attr to ‹"18"› -- which was read back as ‹"19"› -- the
|
|
|
|
|
matcher expected the Example to be valid, but it was invalid instead,
|
|
|
|
|
producing these validation errors:
|
|
|
|
|
|
|
|
|
|
* attr: ["must be equal to 18"]
|
|
|
|
|
|
|
|
|
|
As indicated in the message above, :attr seems to be changing certain
|
|
|
|
|
values as they are set, and this could have something to do with why
|
|
|
|
|
this test is failing. If you've overridden the writer method for this
|
|
|
|
|
attribute, then you may need to change it to make this test pass, or
|
|
|
|
|
do something else entirely.
|
|
|
|
|
MESSAGE
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
) do
|
|
|
|
|
def validation_matcher_scenario_args
|
|
|
|
|
super.deep_merge(validation_options: { equal_to: 18 })
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def configure_validation_matcher(matcher)
|
|
|
|
|
matcher.is_equal_to(18)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-10-28 17:01:26 +00:00
|
|
|
|
context 'when the attribute is a virtual attribute in an ActiveRecord model' do
|
|
|
|
|
it 'accepts' do
|
|
|
|
|
record = build_record_validating_numericality_of_virtual_attribute(
|
|
|
|
|
equal_to: 18,
|
|
|
|
|
)
|
|
|
|
|
expect(record).to validate_numericality.is_equal_to(18)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-10-07 05:12:30 +00:00
|
|
|
|
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
|
2015-10-21 20:39:39 +00:00
|
|
|
|
|
|
|
|
|
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
|
2015-01-21 22:46:26 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
assertion = lambda do
|
|
|
|
|
expect(record).to validate_numericality.is_equal_to(18)
|
|
|
|
|
end
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like a number equal to 18,
|
|
|
|
|
but this could not be proved.
|
2015-12-22 07:00:51 +00:00
|
|
|
|
After setting :attr to ‹"19"›, the matcher expected the Example to be
|
2015-12-13 23:45:24 +00:00
|
|
|
|
invalid, but it was valid instead.
|
|
|
|
|
MESSAGE
|
|
|
|
|
|
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2015-03-29 00:12:24 +00:00
|
|
|
|
end
|
2015-01-21 22:46:26 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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
|
|
|
|
|
)
|
2015-01-21 22:46:26 +00:00
|
|
|
|
expect(record).
|
2015-03-29 00:12:24 +00:00
|
|
|
|
to validate_numericality.
|
|
|
|
|
is_greater_than_or_equal_to(18)
|
|
|
|
|
end
|
2015-10-07 05:12:30 +00:00
|
|
|
|
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-31 01:47:46 +00:00
|
|
|
|
it_supports(
|
|
|
|
|
'ignoring_interference_by_writer',
|
|
|
|
|
tests: {
|
|
|
|
|
reject_if_qualified_but_changing_value_interferes: {
|
|
|
|
|
model_name: 'Example',
|
|
|
|
|
attribute_name: :attr,
|
|
|
|
|
changing_values_with: :next_value,
|
|
|
|
|
expected_message: <<-MESSAGE.strip
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like a number greater than
|
|
|
|
|
or equal to 18, but this could not be proved.
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-31 01:47:46 +00:00
|
|
|
|
After setting :attr to ‹"17"› -- which was read back as ‹"18"› -- the
|
|
|
|
|
matcher expected the Example to be invalid, but it was valid instead.
|
|
|
|
|
|
|
|
|
|
As indicated in the message above, :attr seems to be changing certain
|
|
|
|
|
values as they are set, and this could have something to do with why
|
|
|
|
|
this test is failing. If you've overridden the writer method for this
|
|
|
|
|
attribute, then you may need to change it to make this test pass, or
|
|
|
|
|
do something else entirely.
|
|
|
|
|
MESSAGE
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
) do
|
|
|
|
|
def validation_matcher_scenario_args
|
|
|
|
|
super.deep_merge(
|
|
|
|
|
validation_options: { greater_than_or_equal_to: 18 }
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def configure_validation_matcher(matcher)
|
|
|
|
|
matcher.is_greater_than_or_equal_to(18)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-10-28 17:01:26 +00:00
|
|
|
|
context 'when the attribute is a virtual attribute in an ActiveRecord model' do
|
|
|
|
|
it 'accepts' do
|
|
|
|
|
record = build_record_validating_numericality_of_virtual_attribute(
|
|
|
|
|
greater_than_or_equal_to: 18,
|
|
|
|
|
)
|
|
|
|
|
expect(record).to validate_numericality.
|
|
|
|
|
is_greater_than_or_equal_to(18)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-10-07 05:12:30 +00:00
|
|
|
|
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
|
2015-10-21 20:39:39 +00:00
|
|
|
|
|
|
|
|
|
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
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
assertion = lambda do
|
2015-12-13 23:45:24 +00:00
|
|
|
|
expect(record).to validate_numericality.
|
2015-03-29 00:12:24 +00:00
|
|
|
|
is_greater_than_or_equal_to(18)
|
|
|
|
|
end
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like a number greater than
|
|
|
|
|
or equal to 18, but this could not be proved.
|
2015-12-22 07:00:51 +00:00
|
|
|
|
After setting :attr to ‹"17"›, the matcher expected the Example to be
|
2015-12-13 23:45:24 +00:00
|
|
|
|
invalid, but it was valid instead.
|
|
|
|
|
MESSAGE
|
|
|
|
|
|
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2015-03-29 00:12:24 +00:00
|
|
|
|
end
|
2015-01-21 22:46:26 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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)
|
2015-01-21 22:46:26 +00:00
|
|
|
|
expect(record).
|
2015-03-29 00:12:24 +00:00
|
|
|
|
to validate_numericality.
|
|
|
|
|
is_greater_than(18)
|
|
|
|
|
end
|
2015-10-07 05:12:30 +00:00
|
|
|
|
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-31 01:47:46 +00:00
|
|
|
|
it_supports(
|
|
|
|
|
'ignoring_interference_by_writer',
|
|
|
|
|
tests: {
|
|
|
|
|
reject_if_qualified_but_changing_value_interferes: {
|
|
|
|
|
model_name: 'Example',
|
|
|
|
|
attribute_name: :attr,
|
|
|
|
|
changing_values_with: :next_value,
|
|
|
|
|
expected_message: <<-MESSAGE.strip
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like a number greater than
|
|
|
|
|
18, but this could not be proved.
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-31 01:47:46 +00:00
|
|
|
|
After setting :attr to ‹"18"› -- which was read back as ‹"19"› -- the
|
|
|
|
|
matcher expected the Example to be invalid, but it was valid instead.
|
|
|
|
|
|
|
|
|
|
As indicated in the message above, :attr seems to be changing certain
|
|
|
|
|
values as they are set, and this could have something to do with why
|
|
|
|
|
this test is failing. If you've overridden the writer method for this
|
|
|
|
|
attribute, then you may need to change it to make this test pass, or
|
|
|
|
|
do something else entirely.
|
|
|
|
|
MESSAGE
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
) do
|
|
|
|
|
def validation_matcher_scenario_args
|
|
|
|
|
super.deep_merge(validation_options: { greater_than: 18 })
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def configure_validation_matcher(matcher)
|
|
|
|
|
matcher.is_greater_than(18)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-10-28 17:01:26 +00:00
|
|
|
|
context 'when the attribute is a virtual attribute in an ActiveRecord model' do
|
|
|
|
|
it 'accepts' do
|
|
|
|
|
record = build_record_validating_numericality_of_virtual_attribute(
|
|
|
|
|
greater_than: 18,
|
|
|
|
|
)
|
|
|
|
|
expect(record).to validate_numericality.is_greater_than(18)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-10-07 05:12:30 +00:00
|
|
|
|
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
|
2015-10-21 20:39:39 +00:00
|
|
|
|
|
|
|
|
|
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
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
assertion = lambda do
|
2015-12-13 23:45:24 +00:00
|
|
|
|
expect(record).to validate_numericality.is_greater_than(18)
|
2015-03-29 00:12:24 +00:00
|
|
|
|
end
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like a number greater than
|
|
|
|
|
18, but this could not be proved.
|
2015-12-22 07:00:51 +00:00
|
|
|
|
After setting :attr to ‹"18"›, the matcher expected the Example to be
|
2015-12-13 23:45:24 +00:00
|
|
|
|
invalid, but it was valid instead.
|
|
|
|
|
MESSAGE
|
|
|
|
|
|
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2015-03-29 00:12:24 +00:00
|
|
|
|
end
|
2015-01-21 22:46:26 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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/)
|
2014-02-27 03:26:37 +00:00
|
|
|
|
end
|
2015-03-29 00:12:24 +00:00
|
|
|
|
end
|
2014-02-27 03:26:37 +00:00
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
context 'and validating with a different message' do
|
2015-12-13 23:45:24 +00:00
|
|
|
|
it 'rejects with the correct failure message' do
|
2015-03-29 00:12:24 +00:00
|
|
|
|
record = build_record_validating_numericality(message: 'custom')
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
assertion = lambda do
|
|
|
|
|
expect(record).to validate_numericality.with_message(/wrong/)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like a number, producing a
|
|
|
|
|
custom validation error on failure, but this could not be proved.
|
2015-12-22 07:00:51 +00:00
|
|
|
|
After setting :attr to ‹"abcd"›, the matcher expected the Example to
|
|
|
|
|
be invalid and to produce a validation error matching ‹/wrong/› on
|
|
|
|
|
:attr. The record was indeed invalid, but it produced these validation
|
|
|
|
|
errors instead:
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
* attr: ["custom"]
|
|
|
|
|
MESSAGE
|
|
|
|
|
|
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2014-02-27 03:26:37 +00:00
|
|
|
|
end
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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)
|
2014-02-27 03:26:37 +00:00
|
|
|
|
end
|
2015-03-29 00:12:24 +00:00
|
|
|
|
end
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
context 'and the validation is missing from the model' do
|
|
|
|
|
it 'rejects with the correct failure message' do
|
|
|
|
|
model = define_model_validating_nothing
|
|
|
|
|
|
|
|
|
|
assertion = lambda do
|
|
|
|
|
expect(model.new).to validate_numericality.with_message(/wrong/)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like a number, producing a
|
|
|
|
|
custom validation error on failure, but this could not be proved.
|
2015-12-22 07:00:51 +00:00
|
|
|
|
After setting :attr to ‹"abcd"›, the matcher expected the Example to
|
|
|
|
|
be invalid, but it was valid instead.
|
2015-12-13 23:45:24 +00:00
|
|
|
|
MESSAGE
|
|
|
|
|
|
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
|
|
|
|
end
|
|
|
|
|
end
|
2015-03-29 00:12:24 +00:00
|
|
|
|
end
|
2014-02-27 03:26:37 +00:00
|
|
|
|
|
2014-12-03 22:06:23 +00:00
|
|
|
|
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)
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2014-12-03 22:06:23 +00:00
|
|
|
|
assertion = lambda do
|
|
|
|
|
expect(record).to validate_numericality_of(:attr).strict
|
|
|
|
|
end
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like a number, raising a
|
|
|
|
|
validation exception on failure, but this could not be proved.
|
2015-12-22 07:00:51 +00:00
|
|
|
|
After setting :attr to ‹"abcd"›, the matcher expected the Example to
|
|
|
|
|
be invalid and to raise a validation exception, but the record
|
|
|
|
|
produced validation errors instead.
|
2015-12-13 23:45:24 +00:00
|
|
|
|
MESSAGE
|
|
|
|
|
|
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2014-12-03 22:06:23 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-04-01 06:00:14 +00:00
|
|
|
|
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)
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-01 06:00:14 +00:00
|
|
|
|
assertion = lambda do
|
|
|
|
|
expect(record).to validate_numericality
|
|
|
|
|
end
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like a number, but this
|
|
|
|
|
could not be proved.
|
2015-12-22 07:00:51 +00:00
|
|
|
|
After setting :attr to ‹"abcd"›, the matcher expected the Example to
|
|
|
|
|
be invalid, but it was valid instead.
|
2015-12-13 23:45:24 +00:00
|
|
|
|
MESSAGE
|
|
|
|
|
|
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2015-04-01 06:00:14 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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
|
2014-02-27 03:26:37 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
context 'when the qualifiers do not match the validation options' do
|
2015-04-09 14:27:26 +00:00
|
|
|
|
specify 'such as validating even but testing that only_integer is validated' do
|
2015-03-29 00:12:24 +00:00
|
|
|
|
record = build_record_validating_numericality(
|
|
|
|
|
even: true,
|
|
|
|
|
greater_than: 18
|
|
|
|
|
)
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
assertion = lambda do
|
|
|
|
|
expect(record).
|
|
|
|
|
to validate_numericality.
|
|
|
|
|
only_integer.
|
|
|
|
|
is_greater_than(18)
|
|
|
|
|
end
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like an integer greater
|
|
|
|
|
than 18, but this could not be proved.
|
2015-12-13 23:45:24 +00:00
|
|
|
|
In checking that Example disallows :attr from being a decimal number,
|
2015-12-22 07:00:51 +00:00
|
|
|
|
after setting :attr to ‹"0.1"›, the matcher expected the Example to be
|
2015-12-13 23:45:24 +00:00
|
|
|
|
invalid and to produce the validation error "must be an integer" on
|
|
|
|
|
:attr. The record was indeed invalid, but it produced these validation
|
|
|
|
|
errors instead:
|
|
|
|
|
|
|
|
|
|
* attr: ["must be greater than 18"]
|
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
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2014-02-27 03:26:37 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
specify 'such as not validating only_integer but testing that only_integer is validated' do
|
2015-03-29 00:12:24 +00:00
|
|
|
|
record = build_record_validating_numericality(greater_than: 18)
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
assertion = lambda do
|
|
|
|
|
expect(record).
|
|
|
|
|
to validate_numericality.
|
|
|
|
|
only_integer.
|
|
|
|
|
is_greater_than(18)
|
|
|
|
|
end
|
2015-12-13 23:45:24 +00: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
2015-09-26 05:10:00 +00:00
|
|
|
|
message = <<-MESSAGE.strip_heredoc
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like an integer greater
|
|
|
|
|
than 18, but this could not be proved.
|
2015-12-13 23:45:24 +00:00
|
|
|
|
In checking that Example disallows :attr from being a decimal number,
|
2015-12-22 07:00:51 +00:00
|
|
|
|
after setting :attr to ‹"0.1"›, the matcher expected the Example to be
|
2015-12-13 23:45:24 +00:00
|
|
|
|
invalid and to produce the validation error "must be an integer" on
|
|
|
|
|
:attr. The record was indeed invalid, but it produced these validation
|
|
|
|
|
errors instead:
|
|
|
|
|
|
|
|
|
|
* attr: ["must be greater than 18"]
|
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
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2014-02-27 03:26:37 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
specify 'such as validating greater_than_or_equal_to (+ even) but testing that greater_than is validated' do
|
2015-03-29 00:12:24 +00:00
|
|
|
|
record = build_record_validating_numericality(
|
|
|
|
|
even: true,
|
|
|
|
|
greater_than_or_equal_to: 18
|
|
|
|
|
)
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
assertion = lambda do
|
|
|
|
|
expect(record).
|
|
|
|
|
to validate_numericality.
|
|
|
|
|
even.
|
|
|
|
|
is_greater_than(18)
|
|
|
|
|
end
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like an even number
|
|
|
|
|
greater than 18, but this could not be proved.
|
2015-12-13 23:45:24 +00:00
|
|
|
|
In checking that Example disallows :attr from being a number that is
|
2015-12-22 07:00:51 +00:00
|
|
|
|
not greater than 18, after setting :attr to ‹"18"›, the matcher
|
|
|
|
|
expected the Example to be invalid, but it was valid instead.
|
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
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2014-02-27 03:26:37 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
specify 'such as validating odd (+ greater_than) but testing that even is validated' do
|
2015-03-29 00:12:24 +00:00
|
|
|
|
record = build_record_validating_numericality(
|
|
|
|
|
odd: true,
|
|
|
|
|
greater_than: 18
|
|
|
|
|
)
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
assertion = lambda do
|
|
|
|
|
expect(record).
|
|
|
|
|
to validate_numericality.
|
|
|
|
|
even.
|
|
|
|
|
is_greater_than(18)
|
|
|
|
|
end
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like an even number
|
|
|
|
|
greater than 18, but this could not be proved.
|
2015-12-13 23:45:24 +00:00
|
|
|
|
In checking that Example disallows :attr from being an odd number,
|
2015-12-22 07:00:51 +00:00
|
|
|
|
after setting :attr to ‹"1"›, the matcher expected the Example to be
|
2015-12-13 23:45:24 +00:00
|
|
|
|
invalid and to produce the validation error "must be even" on :attr.
|
|
|
|
|
The record was indeed invalid, but it produced these validation errors
|
|
|
|
|
instead:
|
|
|
|
|
|
|
|
|
|
* attr: ["must be greater than 18"]
|
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
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2014-02-27 03:26:37 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
specify 'such as validating greater_than_or_equal_to (+ odd) but testing that is_less_than_or_equal_to is validated' do
|
2015-03-29 00:12:24 +00:00
|
|
|
|
record = build_record_validating_numericality(
|
|
|
|
|
odd: true,
|
|
|
|
|
greater_than_or_equal_to: 99
|
|
|
|
|
)
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
assertion = lambda do
|
|
|
|
|
expect(record).
|
|
|
|
|
to validate_numericality.
|
|
|
|
|
odd.
|
|
|
|
|
is_less_than_or_equal_to(99)
|
|
|
|
|
end
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like an odd number less
|
|
|
|
|
than or equal to 99, but this could not be proved.
|
2015-12-13 23:45:24 +00:00
|
|
|
|
In checking that Example disallows :attr from being a number that is
|
2015-12-22 07:00:51 +00:00
|
|
|
|
not less than or equal to 99, after setting :attr to ‹"101"›, the
|
2015-12-13 23:45:24 +00:00
|
|
|
|
matcher expected the Example to be invalid, but it was valid instead.
|
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
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2014-02-27 03:26:37 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
specify 'such as validating greater_than_or_equal_to (+ only_integer + less_than) but testing that greater_than is validated' do
|
2015-03-29 00:12:24 +00:00
|
|
|
|
record = build_record_validating_numericality(
|
|
|
|
|
only_integer: true,
|
|
|
|
|
greater_than_or_equal_to: 18,
|
|
|
|
|
less_than: 99
|
|
|
|
|
)
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
assertion = lambda do
|
|
|
|
|
expect(record).
|
|
|
|
|
to validate_numericality.
|
|
|
|
|
only_integer.
|
|
|
|
|
is_greater_than(18).
|
|
|
|
|
is_less_than(99)
|
|
|
|
|
end
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like an integer greater
|
|
|
|
|
than 18 and less than 99, but this could not be proved.
|
2015-12-13 23:45:24 +00:00
|
|
|
|
In checking that Example disallows :attr from being a number that is
|
2015-12-22 07:00:51 +00:00
|
|
|
|
not greater than 18, after setting :attr to ‹"18"›, the matcher
|
|
|
|
|
expected the Example to be invalid, but it was valid instead.
|
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
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2014-02-27 03:26:37 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
context 'when qualifiers match the validation options but the values are different' do
|
2015-04-09 14:27:26 +00:00
|
|
|
|
specify 'such as testing greater_than (+ only_integer) with lower value' do
|
2015-03-29 00:12:24 +00:00
|
|
|
|
record = build_record_validating_numericality(
|
|
|
|
|
only_integer: true,
|
|
|
|
|
greater_than: 19
|
|
|
|
|
)
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
assertion = lambda do
|
|
|
|
|
expect(record).
|
|
|
|
|
to validate_numericality.
|
|
|
|
|
only_integer.
|
|
|
|
|
is_greater_than(18)
|
|
|
|
|
end
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like an integer greater
|
|
|
|
|
than 18, but this could not be proved.
|
2015-12-13 23:45:24 +00:00
|
|
|
|
In checking that Example disallows :attr from being a number that is
|
2015-12-22 07:00:51 +00:00
|
|
|
|
not greater than 18, after setting :attr to ‹"18"›, the matcher
|
|
|
|
|
expected the Example to be invalid and to produce the validation error
|
|
|
|
|
"must be greater than 18" on :attr. The record was indeed invalid, but
|
|
|
|
|
it produced these validation errors instead:
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
* attr: ["must be greater than 19"]
|
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
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2014-02-27 03:26:37 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
specify 'such as testing greater_than (+ only_integer) with higher value' do
|
2015-03-29 00:12:24 +00:00
|
|
|
|
record = build_record_validating_numericality(
|
|
|
|
|
only_integer: true,
|
|
|
|
|
greater_than: 17
|
|
|
|
|
)
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
assertion = lambda do
|
|
|
|
|
expect(record).
|
|
|
|
|
to validate_numericality.
|
|
|
|
|
only_integer.
|
|
|
|
|
is_greater_than(18)
|
|
|
|
|
end
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like an integer greater
|
|
|
|
|
than 18, but this could not be proved.
|
2015-12-13 23:45:24 +00:00
|
|
|
|
In checking that Example disallows :attr from being a number that is
|
2015-12-22 07:00:51 +00:00
|
|
|
|
not greater than 18, after setting :attr to ‹"18"›, the matcher
|
|
|
|
|
expected the Example to be invalid, but it was valid instead.
|
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
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2014-02-27 03:26:37 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
specify 'such as testing greater_than (+ even) with lower value' do
|
2015-03-29 00:12:24 +00:00
|
|
|
|
record = build_record_validating_numericality(
|
|
|
|
|
even: true,
|
|
|
|
|
greater_than: 20
|
|
|
|
|
)
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
assertion = lambda do
|
|
|
|
|
expect(record).
|
|
|
|
|
to validate_numericality.
|
|
|
|
|
even.
|
|
|
|
|
is_greater_than(18)
|
|
|
|
|
end
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like an even number
|
|
|
|
|
greater than 18, but this could not be proved.
|
2015-12-13 23:45:24 +00:00
|
|
|
|
In checking that Example disallows :attr from being a number that is
|
2015-12-22 07:00:51 +00:00
|
|
|
|
not greater than 18, after setting :attr to ‹"18"›, the matcher
|
|
|
|
|
expected the Example to be invalid and to produce the validation error
|
|
|
|
|
"must be greater than 18" on :attr. The record was indeed invalid, but
|
|
|
|
|
it produced these validation errors instead:
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
* attr: ["must be greater than 20"]
|
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
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2014-02-27 03:26:37 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
specify 'such as testing greater than (+ even) with higher value' do
|
2015-03-29 00:12:24 +00:00
|
|
|
|
record = build_record_validating_numericality(
|
|
|
|
|
even: true,
|
|
|
|
|
greater_than: 16
|
|
|
|
|
)
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
assertion = lambda do
|
|
|
|
|
expect(record).
|
|
|
|
|
to validate_numericality.
|
|
|
|
|
even.
|
|
|
|
|
is_greater_than(18)
|
|
|
|
|
end
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like an even number
|
|
|
|
|
greater than 18, but this could not be proved.
|
2015-12-13 23:45:24 +00:00
|
|
|
|
In checking that Example disallows :attr from being a number that is
|
2015-12-22 07:00:51 +00:00
|
|
|
|
not greater than 18, after setting :attr to ‹"18"›, the matcher
|
|
|
|
|
expected the Example to be invalid, but it was valid instead.
|
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
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2014-02-27 03:26:37 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
specify 'such as testing less_than_or_equal_to (+ odd) with lower value' do
|
2015-03-29 00:12:24 +00:00
|
|
|
|
record = build_record_validating_numericality(
|
|
|
|
|
odd: true,
|
|
|
|
|
less_than_or_equal_to: 101
|
|
|
|
|
)
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
assertion = lambda do
|
|
|
|
|
expect(record).
|
|
|
|
|
to validate_numericality.
|
|
|
|
|
odd.
|
|
|
|
|
is_less_than_or_equal_to(99)
|
|
|
|
|
end
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like an odd number less
|
|
|
|
|
than or equal to 99, but this could not be proved.
|
2015-12-13 23:45:24 +00:00
|
|
|
|
In checking that Example disallows :attr from being a number that is
|
2015-12-22 07:00:51 +00:00
|
|
|
|
not less than or equal to 99, after setting :attr to ‹"101"›, the
|
2015-12-13 23:45:24 +00:00
|
|
|
|
matcher expected the Example to be invalid, but it was valid instead.
|
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
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2014-02-27 03:26:37 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
specify 'such as testing less_than_or_equal_to (+ odd) with higher value' do
|
2015-03-29 00:12:24 +00:00
|
|
|
|
record = build_record_validating_numericality(
|
|
|
|
|
odd: true,
|
|
|
|
|
less_than_or_equal_to: 97
|
|
|
|
|
)
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
assertion = lambda do
|
|
|
|
|
expect(record).
|
|
|
|
|
to validate_numericality.
|
|
|
|
|
odd.
|
|
|
|
|
is_less_than_or_equal_to(99)
|
|
|
|
|
end
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like an odd number less
|
|
|
|
|
than or equal to 99, but this could not be proved.
|
2015-12-13 23:45:24 +00:00
|
|
|
|
In checking that Example disallows :attr from being a number that is
|
2015-12-22 07:00:51 +00:00
|
|
|
|
not less than or equal to 99, after setting :attr to ‹"101"›, the
|
2015-12-13 23:45:24 +00:00
|
|
|
|
matcher expected the Example to be invalid and to produce the
|
|
|
|
|
validation error "must be less than or equal to 99" on :attr. The
|
|
|
|
|
record was indeed invalid, but it produced these validation errors
|
|
|
|
|
instead:
|
|
|
|
|
|
|
|
|
|
* attr: ["must be less than or equal to 97"]
|
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
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2014-02-27 03:26:37 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
specify 'such as testing greater_than (+ only_integer + less_than) with lower value' do
|
2015-03-29 00:12:24 +00:00
|
|
|
|
record = build_record_validating_numericality(
|
|
|
|
|
only_integer: true,
|
|
|
|
|
greater_than: 19,
|
|
|
|
|
less_than: 99
|
|
|
|
|
)
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
assertion = lambda do
|
|
|
|
|
expect(record).
|
|
|
|
|
to validate_numericality.
|
|
|
|
|
only_integer.
|
|
|
|
|
is_greater_than(18).
|
|
|
|
|
is_less_than(99)
|
|
|
|
|
end
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like an integer greater
|
|
|
|
|
than 18 and less than 99, but this could not be proved.
|
2015-12-13 23:45:24 +00:00
|
|
|
|
In checking that Example disallows :attr from being a number that is
|
2015-12-22 07:00:51 +00:00
|
|
|
|
not greater than 18, after setting :attr to ‹"18"›, the matcher
|
|
|
|
|
expected the Example to be invalid and to produce the validation error
|
|
|
|
|
"must be greater than 18" on :attr. The record was indeed invalid, but
|
|
|
|
|
it produced these validation errors instead:
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
* attr: ["must be greater than 19"]
|
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
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2014-02-27 03:26:37 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
specify 'such as testing less_than (+ only_integer + greater_than) with higher value' do
|
2015-03-29 00:12:24 +00:00
|
|
|
|
record = build_record_validating_numericality(
|
|
|
|
|
only_integer: true,
|
|
|
|
|
greater_than: 18,
|
|
|
|
|
less_than: 100
|
|
|
|
|
)
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
assertion = lambda do
|
|
|
|
|
expect(record).
|
|
|
|
|
to validate_numericality.
|
|
|
|
|
only_integer.
|
|
|
|
|
is_greater_than(18).
|
|
|
|
|
is_less_than(99)
|
|
|
|
|
end
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
|
|
|
|
message = <<-MESSAGE
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like an integer greater
|
|
|
|
|
than 18 and less than 99, but this could not be proved.
|
2015-12-13 23:45:24 +00:00
|
|
|
|
In checking that Example disallows :attr from being a number that is
|
2015-12-22 07:00:51 +00:00
|
|
|
|
not less than 99, after setting :attr to ‹"100"›, the matcher expected
|
2015-12-13 23:45:24 +00:00
|
|
|
|
the Example to be invalid and to produce the validation error "must be
|
|
|
|
|
less than 99" on :attr. The record was indeed invalid, but it produced
|
|
|
|
|
these validation errors instead:
|
|
|
|
|
|
|
|
|
|
* attr: ["must be less than 100"]
|
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
|
2015-12-13 23:45:24 +00:00
|
|
|
|
|
2015-04-09 14:27:26 +00:00
|
|
|
|
expect(&assertion).to fail_with_message(message)
|
2014-02-27 03:26:37 +00:00
|
|
|
|
end
|
2013-03-19 04:50:39 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2014-04-15 21:16:01 +00:00
|
|
|
|
context 'with large numbers' do
|
|
|
|
|
it do
|
2015-03-29 00:12:24 +00:00
|
|
|
|
record = build_record_validating_numericality(greater_than: 100_000)
|
|
|
|
|
expect(record).to validate_numericality.is_greater_than(100_000)
|
2014-04-15 21:16:01 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it do
|
2015-03-29 00:12:24 +00:00
|
|
|
|
record = build_record_validating_numericality(less_than: 100_000)
|
|
|
|
|
expect(record).to validate_numericality.is_less_than(100_000)
|
2014-04-15 21:16:01 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it do
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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)
|
2014-04-15 21:16:01 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it do
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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)
|
2012-10-16 17:45:06 +00:00
|
|
|
|
end
|
2012-12-20 05:04:27 +00:00
|
|
|
|
end
|
2012-10-16 17:45:06 +00:00
|
|
|
|
|
2013-07-10 17:05:41 +00:00
|
|
|
|
context 'when the subject is stubbed' do
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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
|
2013-07-10 17:05:41 +00:00
|
|
|
|
before_validation :set_attr!
|
|
|
|
|
def set_attr!; self.attr = 5 end
|
2015-03-29 00:12:24 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
record = model.new
|
|
|
|
|
allow(record).to receive(:set_attr!)
|
2013-07-10 17:05:41 +00:00
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
expect(record).to validate_numericality_of(:attr).odd
|
2013-07-10 17:05:41 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-10-07 05:12:30 +00:00
|
|
|
|
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
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-31 01:47:46 +00:00
|
|
|
|
|
|
|
|
|
it_supports(
|
|
|
|
|
'ignoring_interference_by_writer',
|
|
|
|
|
tests: {
|
|
|
|
|
accept_if_qualified_but_changing_value_does_not_interfere: {
|
|
|
|
|
changing_values_with: :next_value,
|
|
|
|
|
},
|
|
|
|
|
reject_if_qualified_but_changing_value_interferes: {
|
|
|
|
|
model_name: 'Example',
|
|
|
|
|
attribute_name: :attr,
|
|
|
|
|
changing_values_with: :numeric_value,
|
|
|
|
|
expected_message: <<-MESSAGE.strip
|
Fix negative versions of validation matchers
When using a validation matcher in the negative, i.e.:
should_not validate_*(...)
as opposed to:
should validate_*(...)
...it's common to receive the following error:
undefined method `attribute_setter' for nil:NilClass
This happens particularly when using a matcher that makes use of
AllowValueMatcher or DisallowValueMatcher internally (which all of the
validation matchers do).
Whenever you make an assertion by using a matcher in the negative as
opposed to the positive, RSpec still calls the `matches?` method for
that matcher; however, the assertion will pass if that returns *false*
as opposed to true. In other words, it just inverts the result.
However, whenever we are using AllowValueMatcher or
DisallowValueMatcher, it doesn't really work to invert the result. like
this. This is because AllowValueMatcher and DisallowValueMatcher,
despite their name, aren't truly opposites of each other.
AllowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations fail, store the value that caused the failure along
with the validation errors and return false
5. Otherwise, return true
However, DisallowValueMatcher performs these steps:
1. Set the attribute on the record to some value
2. Run validations on the record
3. Ask whether validations pass or fail
4. If validations *pass*, store the value that caused the failure along
with some metadata and return false
5. Otherwise, return true
This difference in logic is achieved by having AllowValueMatcher
implement `does_not_match?` and then having DisallowValueMatcher use
this for its positive case and use `matches?` for its negative case.
It's easy to see because of this that `does_not_match?` is not the same
as `!matches?` and vice versa.
So a matcher that makes use of these submatchers internally needs to use
their opposite versions whenever that matcher is used in the negative
case. In other words, all of the matchers need a `does_not_match?` which
is like `matches?`, except that all of the logic is inverted, and in all
the cases in which AllowValueMatcher is used, DisallowValueMatcher needs
to be used.
Doing this ensures that when `failure_message` is called on
AllowValueMatcher or DisallowValueMatcher, step 4 in the list of steps
above stores a proper value that can then be referenced in the failure
message for the validation matcher itself.
2018-09-08 20:59:24 +00:00
|
|
|
|
Expected Example to validate that :attr looks like a number, but this
|
|
|
|
|
could not be proved.
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-31 01:47:46 +00:00
|
|
|
|
After setting :attr to ‹"abcd"› -- which was read back as ‹"1"› -- the
|
|
|
|
|
matcher expected the Example to be invalid, but it was valid instead.
|
|
|
|
|
|
|
|
|
|
As indicated in the message above, :attr seems to be changing certain
|
|
|
|
|
values as they are set, and this could have something to do with why
|
|
|
|
|
this test is failing. If you've overridden the writer method for this
|
|
|
|
|
attribute, then you may need to change it to make this test pass, or
|
|
|
|
|
do something else entirely.
|
|
|
|
|
MESSAGE
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def validation_matcher_scenario_args
|
|
|
|
|
super.deep_merge(model_creator: :active_model)
|
|
|
|
|
end
|
2015-10-07 05:12:30 +00:00
|
|
|
|
end
|
|
|
|
|
|
2014-01-13 01:43:36 +00:00
|
|
|
|
describe '#description' do
|
2015-03-29 00:12:24 +00:00
|
|
|
|
context 'qualified with nothing' do
|
|
|
|
|
it 'describes that it allows numbers' do
|
|
|
|
|
matcher = validate_numericality_of(:attr)
|
2015-12-13 23:45:24 +00:00
|
|
|
|
expect(matcher.description).to eq(
|
|
|
|
|
'validate that :attr looks like a number'
|
|
|
|
|
)
|
2015-03-29 00:12:24 +00:00
|
|
|
|
end
|
2014-01-13 01:43:36 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
context 'qualified with only_integer' do
|
|
|
|
|
it 'describes that it allows integers' do
|
|
|
|
|
matcher = validate_numericality_of(:attr).only_integer
|
2015-12-13 23:45:24 +00:00
|
|
|
|
expect(matcher.description).to eq(
|
|
|
|
|
'validate that :attr looks like an integer'
|
|
|
|
|
)
|
2014-02-27 03:26:37 +00:00
|
|
|
|
end
|
2014-01-13 01:43:36 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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])
|
2015-12-13 23:45:24 +00:00
|
|
|
|
expect(matcher.description).to eq(
|
|
|
|
|
"validate that :attr looks like an #{qualifier[:name]} number"
|
|
|
|
|
)
|
2014-02-27 03:26:37 +00:00
|
|
|
|
end
|
2014-01-13 01:43:36 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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(
|
2015-12-13 23:45:24 +00:00
|
|
|
|
"validate that :attr looks like a number #{comparison_phrase} 18"
|
2014-02-27 03:26:37 +00:00
|
|
|
|
)
|
|
|
|
|
end
|
2014-01-13 01:43:36 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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(
|
2015-12-13 23:45:24 +00:00
|
|
|
|
'validate that :attr looks like an odd number greater than or equal to 18'
|
2015-03-29 00:12:24 +00:00
|
|
|
|
)
|
2014-02-27 03:26:37 +00:00
|
|
|
|
end
|
2014-01-13 01:43:36 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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(
|
2015-12-13 23:45:24 +00:00
|
|
|
|
'validate that :attr looks like an integer greater than 18 and less than or equal to 100'
|
2015-03-29 00:12:24 +00:00
|
|
|
|
)
|
|
|
|
|
end
|
2014-01-13 01:43:36 +00:00
|
|
|
|
end
|
2014-12-03 22:06:23 +00:00
|
|
|
|
|
|
|
|
|
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(
|
2015-12-13 23:45:24 +00:00
|
|
|
|
'validate that :attr looks like a number, raising a validation exception on failure'
|
2014-12-03 22:06:23 +00:00
|
|
|
|
)
|
|
|
|
|
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(
|
2015-12-13 23:45:24 +00:00
|
|
|
|
'validate that :attr looks like a number less than 18, raising a validation exception on failure'
|
2014-12-03 22:06:23 +00:00
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2014-01-13 01:43:36 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
def build_validation_options(args)
|
|
|
|
|
combination = args.fetch(:for)
|
2014-01-13 01:43:36 +00:00
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
combination.each_with_object({}) do |qualifier, hash|
|
|
|
|
|
value = self.class.default_validation_values.fetch(qualifier[:validation_name])
|
|
|
|
|
hash[qualifier[:validation_name]] = value
|
|
|
|
|
end
|
2010-12-13 22:28:59 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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
|
2014-01-13 01:43:36 +00:00
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
def define_model_validating_numericality(options = {})
|
2015-04-01 06:00:14 +00:00
|
|
|
|
attribute_name = options.delete(:attribute_name) { self.attribute_name }
|
2015-10-07 05:12:30 +00:00
|
|
|
|
column_type = options.delete(:column_type) { :string }
|
2015-04-01 06:00:14 +00:00
|
|
|
|
|
2015-10-07 05:12:30 +00:00
|
|
|
|
define_model 'Example', attribute_name => { type: column_type } do |model|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
model.validates_numericality_of(attribute_name, options)
|
|
|
|
|
end
|
2012-10-16 17:45:06 +00:00
|
|
|
|
end
|
2015-01-21 22:46:26 +00:00
|
|
|
|
|
2015-10-28 17:01:26 +00:00
|
|
|
|
def define_model_validating_numericality_of_virtual_attribute(options = {})
|
|
|
|
|
attribute_name = options.delete(:attribute_name) { self.attribute_name }
|
|
|
|
|
|
|
|
|
|
define_model 'Example' do |model|
|
|
|
|
|
model.send(:attr_accessor, attribute_name)
|
|
|
|
|
model.validates_numericality_of(attribute_name, options)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def build_record_validating_numericality_of_virtual_attribute(options = {})
|
|
|
|
|
define_model_validating_numericality_of_virtual_attribute(options).new
|
|
|
|
|
end
|
|
|
|
|
|
2015-03-29 00:12:24 +00:00
|
|
|
|
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
|
Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."
That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.
So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.
But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2015-12-31 01:47:46 +00:00
|
|
|
|
|
|
|
|
|
def validation_matcher_scenario_args
|
|
|
|
|
super.deep_merge(
|
|
|
|
|
matcher_name: :validate_numericality_of,
|
|
|
|
|
model_creator: :active_record
|
|
|
|
|
)
|
|
|
|
|
end
|
2010-12-13 22:28:59 +00:00
|
|
|
|
end
|