To use or not to use assert_predicate with minitest in Ruby
Reading time: 3 minutes
Recently RuboCop started to blame my assert something.booked?
style tests in
minitest and telling me this:
Minitest/AssertPredicate: Prefer using `assert_predicate(something, :booked?)`.
I’ve looked for the corresponding Pull Request and also checked the final Rubocop Minitest Style Guide: Assert Predicate.
According to the documentation, we can read:
Use assert_predicate if expecting to test the predicate on the expected object and on applying predicate returns true. The benefit of using the assert_predicate over the assert or assert_equal is the user friendly error message when assertion fails. – assert_predicate
Compare the difference
First I try the simple assert
and let the test fail
test 'something must be booked before it can be used' do
something = Something.new
assert something.booked?
end
# resulting fail output
Expected true to not be truthy.
Now the version with assert_predicate
:
test 'something must be booked before it can be used' do
something = Something.new
assert_predicate something, :booked?
end
# resulting fail output
Expected <something> to be booked?
If you compare the two outputs, you will see, that using assert_predicate
produces a more readable output, what is also mentioned in the documentation.
How to proceed with such a new rule?
As this is a pretty new (March 2022) RuboCop rule in
rubocop-minitest, it will hit a
lot of projects. For me using plain assert where it is useful like in assert something.booked?
is a rule of thumb, as I left RSpec,
because it has for almost everything a separate command, which I became tired to
learn or lookup. Well, how to proceed?
You can ignore it
You can just ignore the rule and add these lines to your .rubocop.yml file in your project:
Minitest/AssertPredicate:
Enabled: false
Minitest/RefutePredicate:
Enabled: false
Remember to add both rules for assert_predicate
and refute_predicate
or
RuboCop will blame refute something.booked?
too.
You can accept the new rule
If you accept this new rule, it is good to have an up-to-date project with all other rules being already applied. To apply all current rule just run:
rubocop -A
All new rules will be applied to all files, which may be more than the rules you wanted to be applied.
As projects for many reasons may be:
- Not up to date with RuboCop rules on all files.
- The RuboCop update has several new rules included.
- The RuboCop update is a jump on several versions and have a lot of new rules, which hit your project.
You can apply only these two relevant to your project and ask RuboCop to fix them for you:
bundle exec rubocop -A --only Minitest/AssertPredicate
bundle exec rubocop -A --only Minitest/RefutePredicate
Afterward commit them with a corresponding explanation, as it may hit a lot of test files.
My current approach
Most of the time I used simple assert
with ruby code and not test
specific language (like it is more common in RSpec). I
thing a simple assert followed by good named ruby code more readable and less to
think about while trying to understand test code.
In this case assert_predicate
and refute_predicate
produce a better output
on a failed test, but check the same as a simple assert
would do. That’s why I
will use assert_predicate
and refute_predicate
now.