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