thoughtbot--shoulda-matchers/lib/shoulda/matchers/active_record
Elliot Winkler 507dc57004 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-15 13:43:30 -03:00
..
association_matchers belongs_to: Change default req/opt to match global default 2018-01-29 00:03:01 -06:00
uniqueness Documentation updates 2014-12-25 01:13:30 -05:00
accept_nested_attributes_for_matcher.rb Update RSpec test style across docs 2016-06-15 18:02:07 -06:00
association_matcher.rb add index_errors association matcher 2018-04-04 14:29:08 -06:00
association_matchers.rb Extract examples in README to inline documentation 2014-06-20 16:41:27 -06:00
define_enum_for_matcher.rb Add with_prefix and with_suffix to define_enum_for 2018-01-28 00:47:56 -06:00
have_db_column_matcher.rb Update RSpec test style across docs 2016-06-15 18:02:07 -06:00
have_db_index_matcher.rb Fix typo (#1119) 2018-08-10 14:36:12 +01:00
have_readonly_attribute_matcher.rb Update RSpec test style across docs 2016-06-15 18:02:07 -06:00
have_secure_token_matcher.rb Add matcher for has_secure_token 2017-10-15 21:07:58 -04:00
serialize_matcher.rb Update RSpec test style across docs 2016-06-15 18:02:07 -06:00
uniqueness.rb Move uniqueness validation to ActiveRecord module 2014-12-13 17:53:16 -05:00
validate_uniqueness_of_matcher.rb Fix negative versions of validation matchers 2018-09-15 13:43:30 -03:00