Specifying failure message for RSpec expectations
April 12, 2016
When writing the Unit Testing ActiveRecord eager-loading blog post a couple months ago, I noticed that the test failures I was getting while writing were not very helpful. Initially, I had an assertion that looked like this:
expect(restaurant.association(:reviews)).to be_loaded
Since I was writing the test before the implementation — *cough* TDD isn’t dead *cough* — I got the expected failure when running it. However, the failure message was not that friendly after all:
$ rspec spec/controllers/restaurant_controller_spec.rb
Failures:
1) RestaurantsController#index eager loads
Failure/Error: expect(restaurant.association(:reviews)).to be_loaded
expected `#<ActiveRecord::Associations::HasManyAssociation (...) >.loaded?` to return true, got false
After seeing this type of test failure, my first instinct was to replace the assertion with something a bit more explicit (and verbose) to get a better failure message:
expect(restaurant.association(:reviews).loaded?).to be true
Then, running the test looked like this:
$ rspec spec/controllers/restaurant_controller_spec.rb
Failures:
1) RestaurantsController#index eager loads
Failure/Error: expect(restaurant.association(:reviews).loaded?).to be true
expected: true
got: false
Which is better than what we had… but not by much. We can do a lot better than that by using a hidden little gem (um, in the metaphorical sense and not a rubygem) already provided to us by RSpec: passing the failure message as the second parameter to the to
method in the whole expect(...).to
expression.
expect(restaurant.association(:reviews)).to be_loaded, 'Restaurant reviews are not eager loaded'
Let’s run that test again and see what the failure looks like now:
$ rspec spec/controllers/restaurant_controller_spec.rb
Failures:
1) RestaurantsController#index eager loads
Failure/Error: expect(restaurant.association(:reviews).loaded?).to be_truthy
Restaurant reviews are not eager loaded
That’s what I’m talking about! Now I can finally focus on making the test pass.
Notes
- We could’ve achieved something similar with a custom matcher, but that seems a bit too much for this case