Logo: Relish

  1. Sign up
  2. Sign in

Project: RSpec Expectations 2.8

predicate matchers

Ruby objects commonly provide predicate methods:

7.zero?                  # => false
0.zero?                  # => true
[1].empty?               # => false
[].empty?                # => true
{ :a => 5 }.has_key?(:b) # => false
{ :b => 5 }.has_key?(:b) # => true

You could use a basic equality matcher to set expectations on these:

7.zero?.should == true # fails with "expected true, got false (using ==)"

...but RSpec provides dynamic predicate matchers that are more readable and
provide better failure output.

For any predicate method, RSpec gives you a corresponding matcher. Simply
prefix the method with "be_" and remove the question mark. Examples:

7.should_not be_zero       # calls 7.zero?
[].should be_empty         # calls [].empty?
x.should be_multiple_of(3) # calls x.multiple_of?(3)

Alternately, for a predicate method that begins with "has_" like Hash#has_key?,
RSpec allows you to use an alternate form since "be_has_key" makes no sense.

hash.should have_key(:foo)       # calls hash.has_key?(:foo)
array.should_not have_odd_values # calls array.has_odd_values?

In either case, RSpec provides nice, clear error messages, such as:

expected zero? to return true, got false

Any arguments passed to the matcher will be passed on to the predicate method.

Scenarios
should be_zero (based on Fixnum#zero?)
Given
a file named "should_be_zero_spec.rb" with:
describe 0 do
  it { should be_zero }
end

describe 7 do
  it { should be_zero } # deliberate failure
end
When
I run rspec should_be_zero_spec.rb
Then
the output should contain "2 examples, 1 failure"
And
the output should contain "expected zero? to return true, got false"
should_not be_empty (based on Array#empty?)
Given
a file named "should_not_be_empty_spec.rb" with:
describe [1, 2, 3] do
  it { should_not be_empty }
end

describe [] do
  it { should_not be_empty } # deliberate failure
end
When
I run rspec should_not_be_empty_spec.rb
Then
the output should contain "2 examples, 1 failure"
And
the output should contain "expected empty? to return false, got true"
should have_key (based on Hash#has_key?)
Given
a file named "should_have_key_spec.rb" with:
describe Hash do
  subject { { :foo => 7 } }
  it { should have_key(:foo) }
  it { should have_key(:bar) } # deliberate failure
end
When
I run rspec should_have_key_spec.rb
Then
the output should contain "2 examples, 1 failure"
And
the output should contain "expected #has_key?(:bar) to return true, got false"
should_not have_all_string_keys (based on custom #has_all_string_keys? method)
Given
a file named "should_not_have_all_string_keys_spec.rb" with:
class Hash
  def has_all_string_keys?
    keys.all? { |k| String === k }
  end
end

describe Hash do
  context 'with symbol keys' do
    subject { { :foo => 7, :bar => 5 } }
    it { should_not have_all_string_keys }
  end

  context 'with string keys' do
    subject { { 'foo' => 7, 'bar' => 5 } }
    it { should_not have_all_string_keys } # deliberate failure
  end
end
When
I run rspec should_not_have_all_string_keys_spec.rb
Then
the output should contain "2 examples, 1 failure"
And
the output should contain "expected #has_all_string_keys? to return false, got true"
matcher arguments are passed on to the predicate method
Given
a file named "predicate_matcher_argument_spec.rb" with:
class Fixnum
  def multiple_of?(x)
    (self % x).zero?
  end
end

describe 12 do
  it { should be_multiple_of(3) }
  it { should_not be_multiple_of(7) }

  # deliberate failures
  it { should_not be_multiple_of(4) }
  it { should be_multiple_of(5) }
end
When
I run rspec predicate_matcher_argument_spec.rb
Then
the output should contain "4 examples, 2 failures"
And
the output should contain "expected multiple_of?(4) to return false, got true"
And
the output should contain "expected multiple_of?(5) to return true, got false"

Last published over 1 year ago by dchelimsky.