thoughtbot--shoulda-matchers/lib/shoulda/matchers/active_model/validation_matcher
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
..
build_description.rb Fix negative versions of validation matchers 2018-09-15 13:43:30 -03:00