Pitfalls of Rspec boolean matchers

Pitfalls of Rspec boolean matchers

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:
# be_true examples
it{ expect(true).to be_true } # passes

# be_false examples
it{ expect(false).to be_false } # passes
but it’s good to know that the following expectations will also pass:
# be_true examples
it{ expect(1).to be_true } # passes with fixnum
it{ expect("string").to be_true } # passes with string
it{ expect(:symbol).to be_true } # passes with symbol
it{ expect(/pattern/).to be_true } # passes with regular expression
it{ expect(Time).to be_true } # passes with class

# be_false examples
it{ expect(nil).to be_false } # passes
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:
# eql(true) examples
it{ expect(1).to eql(true) } # fails with fixnum
it{ expect("string").to eql(true) } # fails with string
it{ expect(:symbol).to eql(true) } # fails with symbol
it{ expect(/pattern/).to eql(true) } # fails with regular expression
it{ expect(Time).to eql(true) } # fails with class

# eql(false) examples
it{ expect(nil).to eql(false) } # fails

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:
Class TestClass
  def self.empty?
    true
  end
end
and Rspec file contains:
it{ expect(TestClass).to be_empty } # passes
Everything works fine, but let’s do changes in our `empty?` method and return some object instead of `Boolean`:
Class TestClass
  def self.empty?
    'true'
  end
end
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:
subject{ TestClass.empty? }

it{ should eql(true) } # fails

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