pitfall

Pitfalls of Rspec boolean matchers

| 5 Comments

Rspec provides a bunch of build-in matchers. They include matchers that help us work with boolean values. But not always these matchers are safe to use.

be_true and be_false matchers

I guess everybody used RSpec matchers be_true and be_false. There are a few examples with them:

but it’s good to know that the following expectations will also pass:

This happens because be_true and be_false matchers consider nil and false to be false and anything else to be true. To make tighter comparison and avoid potential issues use eql matcher:

Predicate matchers

Rspec provides predicate matchers for each ruby method which ends with ‘?’. For instance, we have a class with method empty? which returns true:

and Rspec file contains:

Everything works fine, but let’s do changes in our empty? method and return some object instead of Boolean:

and our test still passes even when we return something different from true. As be_true and be_false matchers, predicate matches consider nil and false to be false and anything else to be true.
To avoid potentials issues with this behaviour we also should make tighter comparison:

Summary

While testing boolean attributes or methods that return boolean value, try to avoid matchers described above.

UPD
Since RSpec 3 was released, this article is actual for RSpec 2

Share
* Railsware is a premium software development consulting company, focused on delivering great web and mobile applications. Learn more about us.
  • Andrius Chamentauskas

    I completely disagree with you. There may be times where you want to test that method returns true or false explicitly, however most of the time you want method to return what’s in ruby considered truthy (not false, not nil) or falsy (false and nil). These matchers are perfect for 99% of the time where explicit boolean is not required.

  • mcribs

    Huge +1. be_true and be_false to me violate a principle of least surprise.

  • NoviceCodeNinja

    it{ expect(false).to be_false } does not pass, as shown in your example. I’m using rspec 3x. Instead, I get… “Failure/Error: it{ expect(false).to be_false }
    expected false to respond to false?

    Instead, this passes: expect(false).to be false

Want to get more of Railsware blog?

RSS FEED

We're always ready to help!

CONTACT US