46

In RSpec: Can I attach a message to a check the same way as I would do in xUnit style test frameworks? How?

assert_equal value1, value2, 'something is wrong'
BinaryButterfly
  • 18,137
  • 13
  • 50
  • 91
Alexey
  • 9,197
  • 5
  • 64
  • 76

3 Answers3

62

For RSpec 3+:

The message could be customized as a string or using a proc(check the reference).

expect(1).to eq(2), 'one is not two!'

Customized message RSpec tries to provide useful failure messages, but for cases in which you want more specific information, you can define your own message right in the example. This works for any matcher other than the operator matchers.

source @ relishapp


For older RSpec versions

should and should_not take a second argument (message) that overrides the matcher’s default message.

1.should be(2), 'one is not two!'

The default messages are usually pretty useful though.

BinaryButterfly
  • 18,137
  • 13
  • 50
  • 91
Chris Johnsen
  • 214,407
  • 26
  • 209
  • 186
  • 4
    How does one do it with `#should ==`? – Translunar Apr 02 '13 at 19:46
  • 1
    @mohawkjohn: It seems like it would be something like `1.should(nil, 'one is not two!') == 2` (ick), but that does work because the `==` operator matcher looks like it always generates its own message. – Chris Johnsen Apr 03 '13 at 02:16
  • 5
    you can also use `eq` instead of `==` i.e. `1.should eq(nil), 'one is not two!'` – house9 Jun 19 '13 at 02:57
29

In RSpec, it's the matcher's job to print a sensible failure message. The generic matchers that ship with RSpec can obviously only print generic non-descript failure messages, since they don't know anything about your particular domain. That's why it is recommended that you write your own domain-specific matchers, which will give you both more readable tests and more readable failure messages.

Here's an example from the RSpec documentation:

require 'rspec/expectations'

RSpec::Matchers.define :be_a_multiple_of do |expected|
  match do |actual|
    (actual % expected).zero?
  end
  failure_message_for_should do |actual|
    "expected that #{actual} would be a multiple of #{expected}"
  end
  failure_message_for_should_not do |actual|
    "expected that #{actual} would not be a multiple of #{expected}"
  end
  description do
    "be multiple of #{expected}"
  end
end

Note: only match is required, the others will be generated automatically. However, the whole point of your question is of course that you do not like the default messages, so you need to at least also define failure_message_for_should.

Also, you can define match_for_should and match_for_should_not instead of match if you need different logic in the positive and negative case.

As @Chris Johnsen shows, you can also explicitly pass a message to the expectation. However, you run the risk of losing the readability advantages.

Compare this:

user.permissions.should be(42), 'user does not have administrative rights'

with this:

user.should have_administrative_rights

That would (roughly) be implemented like this:

require 'rspec/expectations'

RSpec::Matchers.define :have_administrative_rights do
  match do |thing|
    thing.permissions == 42
  end
  failure_message_for_should do |actual|
    'user does not have administrative rights'
  end
  failure_message_for_should_not do |actual|
    'user has administrative rights'
  end
end
Phil Frost
  • 3,668
  • 21
  • 29
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • 1
    Thanks, i didn't know that standard way of defining matchers is that easy. Though I prefer be "lazy": to factor out such things into separate named entities when they appear at least twice or remarkably simplify context where they are used. – Alexey Nov 15 '10 at 16:07
6

In my case it was a problem of parenthesis:

        expect(coder.is_partial?(v)).to eq p, "expected #{v} for #{p}"

this resulted in a wrong number of arguments, while the correct way is:

        expect(coder.is_partial?(v)).to eq(p), "expected #{v} for #{p}"
BPH
  • 527
  • 6
  • 10