Commit Graph

16 Commits

Author SHA1 Message Date
Elliot Winkler afa6a7b666 Update RSpec test style across docs
Instead of using

    describe Foo do
      # ...
    end

use

    RSpec.describe Foo, type: :model do
      # ...
    end

instead. This is not exactly official, as the former style still works,
but the latter style is highly suggested in RSpec documentation and the
like, so this is what people are used to.

[ci skip]
2016-06-15 18:02:07 -06:00
Elliot Winkler 663c2f2c4d Remove most docs for ignoring_interference_by_writer
Since `ignoring_interference_by_writer` is on by default now, we don't
need to explicitly document it (unless someone wants to turn it off, but
that's unlikely).

[ci skip]
2016-01-10 23:19:47 -07:00
Elliot Winkler 5532f4359a Enable ignoring_interference_by_writer by default
Forcing people to add ignoring_interference_by_writer for each and every
case in which an attribute changes incoming values is pretty obnoxious
on our part (for instance, when using the numericality matcher against
an integer column + `only_integer`). So now, it's enabled by default.
This effectively means that people should never get
AtttributeChangedValueErrors again.
2016-01-05 00:58:17 -07:00
Elliot Winkler 1189934806 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.
2016-01-05 00:58:16 -07:00
Elliot Winkler 2962112114 allow_value: pre-set attributes before validation
While attempting to add support for `ignoring_interference_by_writer` to
the confirmation matcher, I was noticing that there are two attributes
we are concerned with: the attribute under test, and the confirmation
attribute -- for instance, `password` and `password_confirmation`. The
way that the matcher works, `password_confirmation` is set first on the
record before `password` is set, and then the whole record is validated.
This is fine, but I also noticed that `allow_value` has a specific way
of setting attributes -- not only does it check whether the attribute
being set exists and fail properly if it is does not, but it also
raises a CouldNotSetAttribute error if the attribute changes incoming
values. This logic needs to be performed on both `password_confirmation`
as well as `password`.

With that in mind, `allow_value` now supports a `values_to_preset=`
writer method which allows one to assign additional attributes unrelated
to the one being tested prior to validation. This will be used by the
confirmation matcher in a future commit.

This means that `allow_value` now operates in two steps:

1. Set attributes unrelated to the test, raising an error if any of the
   attributes do not exist on the model.
2. Set the attribute under test to one or more values, raising an error
   if the attribute does not exist, then running validations on the
   record, failing with an appropriate error message if the validations
   fail.

Note that the second step is similar to the first, although there are
more things involved. To that end, `allow_value` has been completely
refactored so that the logic for setting and validating attributes
happens in other places. Specifically, the core logic to set an
attribute (and capture the results) is located in a new AttributeSetter
class.

Also, the CouldNotSetAttributeError class has been moved to a namespace
and renamed to AttributeChangedValueError.

Finally, this commit fixes DisallowValueMatcher so that it is the true
opposite of AllowValueMatcher: DVM#matches? calls AVM#does_not_match?
and DVM#does_not_match? calls AVM#matches?.
2015-12-30 21:34:02 -05:00
Elliot Winkler db19f772f9 Refactor presence matcher
This is part of a collection of commits that aim to improve failure
messages across the board, in order to make matchers easier to debug
when something goes wrong.

* Make the description of the matcher more readable.
* Fix or fill in tests involving failure messages and descriptions to
  match these changes and recent changes to ValidationMatcher and
  allow_value.
2015-12-13 20:22:23 -07:00
Elliot Winkler 142366ef16 Refer to Minitest in docs over Test::Unit
Minitest has been used instead of Test::Unit for quite some time now,
let's get with the times.

[ci skip]
2015-09-30 13:18:23 -06:00
Elliot Winkler 4b941254fb Fix presence matcher to work with serialized attrs
Why:

* Given a model with an attribute that typecasts its incoming values to
  a collection type such as array or hash (and assuming that the default
  value is empty), the presence matcher failed to run successfully.
  Because we assert that the attribute `should_not allow_value(nil)`,
  and since nil is being typecast to an empty array or hash,
  `allow_value` was raising CouldNotSetAttributeError.

To satisfy the above:

* Rescue CouldNotSetAttributeError; if it occurs, assert that the actual
  value of the attribute is blank instead of the given value.
2015-09-29 22:39:16 -06:00
Anthony Navarre + Elliot Winkler eaaa2d83e5 allow_value: Raise error if attr sets value differently
`allow_value` will now raise a CouldNotSetAttribute error if the
attribute in question cannot be changed from a non-nil value to a nil
value, or vice versa. In other words, these are the exact cases in which
the error will occur:

* If you're testing whether the attribute allows `nil`, but the
  attribute detects and ignores nil. (For instance, you have a model
  that `has_secure_password`. This will add a #password= method to your
  model that is defined in a such a way that you cannot clear the
  password by setting it to nil -- nothing happens.)
* If you're testing whether the attribute allows a non-nil value, but
  the attribute fails to set that value. (For instance, you have an
  ActiveRecord model. If ActiveRecord cannot typecast the value in the
  context of the column, then it will do nothing, and the attribute will be
  effectively set to nil.)

What's the reasoning behind this change? Simply put, if you are assuming
that the attribute is changing but in fact it is not, then the test
you're writing isn't the test that actually gets run. We feel that this
is dishonest and produces an invalid test.
2015-02-17 23:09:56 -07:00
Mauro George 51283c0450 Add documentation to validates_presence_of on qualifier
[ci skip]
2014-12-13 17:56:12 -05:00
Elliot Winkler c22d7c89e0 Extract examples in README to inline documentation 2014-06-20 16:41:27 -06:00
Elliot Winkler a21426c2d6 Raise CouldNotSetPasswordError more safely
It seems that adding a restriction to allow_value to raise
CouldNotClearAttribute if nil cannot be set on the attribute in question
broke a lot of people's tests. Really the only reason we added this was
for validate_presence_of -- it rescued CouldNotClearAttribute and
re-raised it as CouldNotSetPasswordError, but only for passwords and
only if has_secure_password was being used. So, I've figured out another
way of performing this check inside of validate_presence_of only to
prevent tests that have nothing to do with from breaking unnecessarily.
2014-04-16 12:17:58 -06:00
Elliot Winkler 6173a160ac Validating presence of a secure password may raise an error
If you have a model that declares `has_secure_password` and you also
have a presence validation on the password, and you write a test against
this validation using an instance of your model where the password is
already set, then your test will fail. This is because
has_secure_password (at least on Rails 4) defines #password= such that
if it is given nil, then the password will not be overwritten with nil.
This interferes with how our validate_presence_of matcher works.

Unfortunately there is not a great way to get around this (using
\#write_attribute won't work, either). So in this case we raise a
helpful error message that instructs the user to use an empty record
against `validates_presence_of`.
2014-02-28 20:31:41 -07:00
Gabe Berke-Williams fbc4fd3a2d Whitespace. 2012-05-07 10:26:35 -04:00
Gabe Berke-Williams 5d1027b180 Refactor method. 2012-04-24 17:01:50 -05:00
Markus Schwed 2a54fdb28d Seperate ActiveRecord and ActiveModel related matchers 2011-05-06 15:56:36 +02:00