1
0
Fork 0
mirror of https://github.com/thoughtbot/shoulda-matchers.git synced 2022-11-09 12:01:38 -05:00
thoughtbot--shoulda-matchers/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb

642 lines
19 KiB
Ruby
Raw Normal View History

module Shoulda
2010-12-15 17:34:19 -05:00
module Matchers
module ActiveModel
# The `validate_numericality_of` matcher tests usage of the
# `validates_numericality_of` validation.
#
# class Person
# include ActiveModel::Model
# attr_accessor :gpa
#
# validates_numericality_of :gpa
# end
#
# # RSpec
# RSpec.describe Person, type: :model do
# it { should validate_numericality_of(:gpa) }
# end
#
# # Minitest (Shoulda)
# class PersonTest < ActiveSupport::TestCase
# should validate_numericality_of(:gpa)
# end
#
# #### Qualifiers
#
# ##### on
#
# Use `on` if your validation applies only under a certain context.
#
# class Person
# include ActiveModel::Model
# attr_accessor :number_of_dependents
#
# validates_numericality_of :number_of_dependents, on: :create
# end
#
# # RSpec
# RSpec.describe Person, type: :model do
# it do
# should validate_numericality_of(:number_of_dependents).
# on(:create)
# end
# end
#
# # Minitest (Shoulda)
# class PersonTest < ActiveSupport::TestCase
# should validate_numericality_of(:number_of_dependents).on(:create)
# end
#
# ##### only_integer
#
# Use `only_integer` to test usage of the `:only_integer` option. This
# asserts that your attribute only allows integer numbers and disallows
# non-integer ones.
#
# class Person
# include ActiveModel::Model
# attr_accessor :age
#
# validates_numericality_of :age, only_integer: true
# end
#
# # RSpec
# RSpec.describe Person, type: :model do
# it { should validate_numericality_of(:age).only_integer }
# end
#
# # Minitest (Shoulda)
# class PersonTest < ActiveSupport::TestCase
# should validate_numericality_of(:age).only_integer
# end
#
# ##### is_less_than
#
# Use `is_less_than` to test usage of the the `:less_than` option. This
# asserts that the attribute can take a number which is less than the
# given value and cannot take a number which is greater than or equal to
# it.
#
# class Person
# include ActiveModel::Model
# attr_accessor :number_of_cars
#
# validates_numericality_of :number_of_cars, less_than: 2
# end
#
# # RSpec
# RSpec.describe Person, type: :model do
# it do
# should validate_numericality_of(:number_of_cars).
# is_less_than(2)
# end
# end
#
# # Minitest (Shoulda)
# class PersonTest < ActiveSupport::TestCase
# should validate_numericality_of(:number_of_cars).
# is_less_than(2)
# end
#
# ##### is_less_than_or_equal_to
#
# Use `is_less_than_or_equal_to` to test usage of the
# `:less_than_or_equal_to` option. This asserts that the attribute can
# take a number which is less than or equal to the given value and cannot
# take a number which is greater than it.
#
# class Person
# include ActiveModel::Model
# attr_accessor :birth_year
#
# validates_numericality_of :birth_year, less_than_or_equal_to: 1987
# end
#
# # RSpec
# RSpec.describe Person, type: :model do
# it do
# should validate_numericality_of(:birth_year).
# is_less_than_or_equal_to(1987)
# end
# end
#
# # Minitest (Shoulda)
# class PersonTest < ActiveSupport::TestCase
# should validate_numericality_of(:birth_year).
# is_less_than_or_equal_to(1987)
# end
#
# ##### is_equal_to
#
# Use `is_equal_to` to test usage of the `:equal_to` option. This asserts
# that the attribute can take a number which is equal to the given value
# and cannot take a number which is not equal.
#
# class Person
# include ActiveModel::Model
# attr_accessor :weight
#
# validates_numericality_of :weight, equal_to: 150
# end
#
# # RSpec
# RSpec.describe Person, type: :model do
# it { should validate_numericality_of(:weight).is_equal_to(150) }
# end
#
# # Minitest (Shoulda)
# class PersonTest < ActiveSupport::TestCase
# should validate_numericality_of(:weight).is_equal_to(150)
# end
#
# ##### is_greater_than_or_equal_to
#
# Use `is_greater_than_or_equal_to` to test usage of the
# `:greater_than_or_equal_to` option. This asserts that the attribute can
# take a number which is greater than or equal to the given value and
# cannot take a number which is less than it.
#
# class Person
# include ActiveModel::Model
# attr_accessor :height
#
# validates_numericality_of :height, greater_than_or_equal_to: 55
# end
#
# # RSpec
# RSpec.describe Person, type: :model do
# it do
# should validate_numericality_of(:height).
# is_greater_than_or_equal_to(55)
# end
# end
#
# # Minitest (Shoulda)
# class PersonTest < ActiveSupport::TestCase
# should validate_numericality_of(:height).
# is_greater_than_or_equal_to(55)
# end
#
# ##### is_greater_than
#
2015-10-07 08:42:25 -04:00
# Use `is_greater_than` to test usage of the `:greater_than` option.
# This asserts that the attribute can take a number which is greater than
# the given value and cannot take a number less than or equal to it.
#
# class Person
# include ActiveModel::Model
# attr_accessor :legal_age
#
# validates_numericality_of :legal_age, greater_than: 21
# end
#
# # RSpec
# RSpec.describe Person, type: :model do
# it do
# should validate_numericality_of(:legal_age).
# is_greater_than(21)
# end
# end
#
# # Minitest (Shoulda)
# class PersonTest < ActiveSupport::TestCase
# should validate_numericality_of(:legal_age).
# is_greater_than(21)
# end
#
# ##### even
#
# Use `even` to test usage of the `:even` option. This asserts that the
# attribute can take odd numbers and cannot take even ones.
#
# class Person
# include ActiveModel::Model
# attr_accessor :birth_month
#
# validates_numericality_of :birth_month, even: true
# end
#
# # RSpec
# RSpec.describe Person, type: :model do
# it { should validate_numericality_of(:birth_month).even }
# end
#
# # Minitest (Shoulda)
# class PersonTest < ActiveSupport::TestCase
# should validate_numericality_of(:birth_month).even
# end
#
# ##### odd
#
# Use `odd` to test usage of the `:odd` option. This asserts that the
# attribute can take a number which is odd and cannot take a number which
# is even.
#
# class Person
# include ActiveModel::Model
# attr_accessor :birth_day
#
# validates_numericality_of :birth_day, odd: true
# end
#
# # RSpec
# RSpec.describe Person, type: :model do
# it { should validate_numericality_of(:birth_day).odd }
# end
#
# # Minitest (Shoulda)
# class PersonTest < ActiveSupport::TestCase
# should validate_numericality_of(:birth_day).odd
# end
#
# ##### with_message
#
# Use `with_message` if you are using a custom validation message.
#
# class Person
# include ActiveModel::Model
# attr_accessor :number_of_dependents
#
# validates_numericality_of :number_of_dependents,
# message: 'Number of dependents must be a number'
# end
#
# # RSpec
# RSpec.describe Person, type: :model do
# it do
# should validate_numericality_of(:number_of_dependents).
# with_message('Number of dependents must be a number')
# end
# end
#
# # Minitest (Shoulda)
# class PersonTest < ActiveSupport::TestCase
# should validate_numericality_of(:number_of_dependents).
# with_message('Number of dependents must be a number')
# end
#
# ##### allow_nil
#
# Use `allow_nil` to assert that the attribute allows nil.
#
# class Post
# include ActiveModel::Model
# attr_accessor :age
#
# validates_numericality_of :age, allow_nil: true
# end
#
# # RSpec
# RSpec.describe Post, type: :model do
# it { should validate_numericality_of(:age).allow_nil }
# end
#
# # Minitest (Shoulda)
# class PostTest < ActiveSupport::TestCase
# should validate_numericality_of(:age).allow_nil
# end
#
# @return [ValidateNumericalityOfMatcher]
2010-12-15 17:34:19 -05:00
#
def validate_numericality_of(attr)
ValidateNumericalityOfMatcher.new(attr)
end
# @private
class ValidateNumericalityOfMatcher
NUMERIC_NAME = 'number'
NON_NUMERIC_VALUE = 'abcd'
DEFAULT_DIFF_TO_COMPARE = 1
include Qualifiers::IgnoringInterferenceByWriter
attr_reader :diff_to_compare
2012-04-24 18:00:26 -04:00
def initialize(attribute)
super
@attribute = attribute
@submatchers = []
@diff_to_compare = DEFAULT_DIFF_TO_COMPARE
@expects_custom_validation_message = false
@expects_to_allow_nil = false
@expects_strict = false
@allowed_type_adjective = nil
@allowed_type_name = 'number'
@context = nil
@expected_message = nil
2012-04-24 18:00:26 -04:00
end
def strict
@expects_strict = true
self
end
def expects_strict?
@expects_strict
end
def only_integer
prepare_submatcher(
NumericalityMatchers::OnlyIntegerMatcher.new(self, @attribute)
)
self
end
def allow_nil
@expects_to_allow_nil = true
prepare_submatcher(
AllowValueMatcher.new(nil)
.for(@attribute)
.with_message(:not_a_number)
)
self
end
def expects_to_allow_nil?
@expects_to_allow_nil
end
def odd
prepare_submatcher(
NumericalityMatchers::OddNumberMatcher.new(self, @attribute)
)
self
end
def even
prepare_submatcher(
NumericalityMatchers::EvenNumberMatcher.new(self, @attribute)
)
self
end
def is_greater_than(value)
prepare_submatcher(comparison_matcher_for(value, :>).for(@attribute))
self
end
def is_greater_than_or_equal_to(value)
prepare_submatcher(comparison_matcher_for(value, :>=).for(@attribute))
self
end
def is_equal_to(value)
prepare_submatcher(comparison_matcher_for(value, :==).for(@attribute))
self
end
def is_less_than(value)
prepare_submatcher(comparison_matcher_for(value, :<).for(@attribute))
self
end
def is_less_than_or_equal_to(value)
prepare_submatcher(comparison_matcher_for(value, :<=).for(@attribute))
self
end
2010-12-15 17:34:19 -05:00
def with_message(message)
@expects_custom_validation_message = true
@expected_message = message
2010-12-15 17:34:19 -05:00
self
end
def expects_custom_validation_message?
@expects_custom_validation_message
end
def on(context)
@context = context
self
2010-12-15 17:34:19 -05:00
end
def matches?(subject)
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 16:59:24 -04:00
matches_or_does_not_match?(subject)
first_submatcher_that_fails_to_match.nil?
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 16:59:24 -04:00
def does_not_match?(subject)
matches_or_does_not_match?(subject)
first_submatcher_that_fails_to_not_match.nil?
2010-12-15 17:34:19 -05:00
end
def simple_description
description = ''
description << "validate that :#{@attribute} looks like "
description << Shoulda::Matchers::Util.a_or_an(full_allowed_type)
if comparison_descriptions.present?
description << ' ' + comparison_descriptions
end
description
end
def description
ValidationMatcher::BuildDescription.call(self, simple_description)
end
def failure_message
overall_failure_message.dup.tap do |message|
message << "\n"
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 16:59:24 -04:00
message << failure_message_for_first_submatcher_that_fails_to_match
end
end
def failure_message_when_negated
overall_failure_message_when_negated.dup.tap do |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 16:59:24 -04:00
message << "\n"
message << failure_message_for_first_submatcher_that_fails_to_not_match
end
2010-12-15 17:34:19 -05:00
end
def given_numeric_column?
attribute_is_active_record_column? &&
[:integer, :float, :decimal].include?(column_type)
end
private
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 16:59:24 -04:00
def matches_or_does_not_match?(subject)
@subject = subject
@number_of_submatchers = @submatchers.size
add_disallow_value_matcher
qualify_submatchers
end
def overall_failure_message
Shoulda::Matchers.word_wrap(
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 16:59:24 -04:00
"Expected #{model.name} to #{description}, but this could not " +
'be proved.'
)
end
def overall_failure_message_when_negated
Shoulda::Matchers.word_wrap(
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 16:59:24 -04:00
"Expected #{model.name} not to #{description}, but this could not " +
'be proved.'
)
end
def attribute_is_active_record_column?
columns_hash.key?(@attribute.to_s)
end
def column_type
columns_hash[@attribute.to_s].type
end
def columns_hash
if @subject.class.respond_to?(:columns_hash)
@subject.class.columns_hash
else
{}
end
end
def add_disallow_value_matcher
disallow_value_matcher = DisallowValueMatcher.
new(NON_NUMERIC_VALUE).
for(@attribute).
with_message(:not_a_number)
add_submatcher(disallow_value_matcher)
2012-04-03 20:20:50 -04:00
end
def prepare_submatcher(submatcher)
add_submatcher(submatcher)
2015-12-27 00:37:15 -05:00
submatcher
end
def comparison_matcher_for(value, operator)
2015-12-27 00:37:15 -05:00
NumericalityMatchers::ComparisonMatcher.
new(self, value, operator).
for(@attribute)
end
def add_submatcher(submatcher)
if submatcher.respond_to?(:allowed_type_name)
@allowed_type_name = submatcher.allowed_type_name
end
if submatcher.respond_to?(:allowed_type_adjective)
@allowed_type_adjective = submatcher.allowed_type_adjective
end
2015-12-27 00:37:15 -05:00
if submatcher.respond_to?(:diff_to_compare)
@diff_to_compare = [@diff_to_compare, submatcher.diff_to_compare].max
end
@submatchers << submatcher
end
def qualify_submatchers
@submatchers.each do |submatcher|
if @expects_strict
submatcher.strict(@expects_strict)
end
if @expected_message.present?
submatcher.with_message(@expected_message)
end
if @context
submatcher.on(@context)
end
submatcher.ignoring_interference_by_writer(
ignore_interference_by_writer
)
end
end
def number_of_submatchers_for_failure_message
if has_been_qualified?
@submatchers.size - 1
else
@submatchers.size
end
end
def has_been_qualified?
@submatchers.any? do |submatcher|
Shoulda::Matchers::RailsShim.parent_of(submatcher.class) ==
NumericalityMatchers
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 16:59:24 -04:00
def first_submatcher_that_fails_to_match
@_failing_submatchers ||= @submatchers.detect do |submatcher|
!submatcher.matches?(@subject)
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 16:59:24 -04:00
def first_submatcher_that_fails_to_not_match
@_failing_submatchers ||= @submatchers.detect do |submatcher|
!submatcher.does_not_match?(@subject)
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 16:59:24 -04:00
def failure_message_for_first_submatcher_that_fails_to_match
build_submatcher_failure_message_for(
first_submatcher_that_fails_to_match,
:failure_message
)
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 16:59:24 -04:00
def failure_message_for_first_submatcher_that_fails_to_not_match
build_submatcher_failure_message_for(
first_submatcher_that_fails_to_not_match,
:failure_message_when_negated
)
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 16:59:24 -04:00
def build_submatcher_failure_message_for(
submatcher,
failure_message_method
)
failure_message = submatcher.public_send(failure_message_method)
submatcher_description = submatcher.simple_description.
sub(/\bvalidate that\b/, 'validates').
sub(/\bdisallow\b/, 'disallows').
sub(/\ballow\b/, 'allows')
submatcher_message =
if number_of_submatchers_for_failure_message > 1
"In checking that #{model.name} #{submatcher_description}, " +
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 16:59:24 -04:00
failure_message[0].downcase +
failure_message[1..-1]
else
failure_message
end
Shoulda::Matchers.word_wrap(submatcher_message, indent: 2)
end
def full_allowed_type
"#{@allowed_type_adjective} #{@allowed_type_name}".strip
end
def comparison_descriptions
description_array = submatcher_comparison_descriptions
description_array.empty? ? '' : submatcher_comparison_descriptions.join(' and ')
end
def submatcher_comparison_descriptions
@submatchers.inject([]) do |arr, submatcher|
if submatcher.respond_to? :comparison_description
arr << submatcher.comparison_description
end
arr
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 16:59:24 -04:00
def model
@subject.class
end
end
2010-12-15 17:34:19 -05:00
end
end
end