Read more

Custom error messages in RSpec or Cucumber steps

Arne Hartherz
February 11, 2014Software engineer at makandra GmbH

Sometimes you have a test expectation but actually want a better error message in case of a failure. Here is how to do that.

Background

Illustration online protection

Rails professionals since 2007

Our laser focus on a single technology has made us a leader in this space. Need help?

  • We build a solid first version of your product
  • We train your development team
  • We rescue your project in trouble
Read more Show archive.org snapshot

Consider this test:

expect(User.last).to be_present

In case of an error, it will fail with a not-so-helpful error message:

expected present? to return true, got false (Spec::Expectations::ExpectationNotMetError)

Solution

That can be fixed easily. RSpec expectations allow you to pass an error message like this:

expect(User.last).to be_present, 'Could not find a user!'

Now your test will fail with an actually helpful error message:

Could not find a user! (Spec::Expectations::ExpectationNotMetError)

Note that you may also pass a block, if necessary.

Why is this cool?

One might argue why we would want to have better error messages for test expectations when our tests are supposed to be always passing. Well, what if they don't pass at some point? Have you ever had to dig into a test just to find out why it was failing? If that test had actually told what it was expecting, you did not have to do it.

Even more so: Consider an expectation that is actually more complex than the one above. Maybe you are looking at an array of items and want to check only a subset of them. Instead of a simple true/false error message, you could also have a message that shows what the expectation was working on, and what failed:

Found 0 instead of 2 matching events:
[]
All events:
[{"gtm.start"=>1234567890, "event"=>"gtm.js"}, {"event"=>"gtm.dom"}, {"event"=>"gtm.load"}]
(Spec::Expectations::ExpectationNotMetError)

And the best thing: Since you are using RSpec expectations in your Cucumber steps most of the time, you can do the same there.

Caveats

If you are using the legacy should syntax, you can't put custom error messages when comparing equality with ==. For that, you might want to use eq instead:

foo.should == 42, 'Not a valid answer' # does NOT work
foo.should eq(42), 'Not a valid answer'

Inequality is trickier, and uglier. You might want to wrap your expectation and compare against a boolean, maybe like this:

expect { foo > 23 }.to be_true, 'Not a valid answer'

If things get too tricky, you could be better off writing a custom RSpec matcher.

Posted by Arne Hartherz to makandra dev (2014-02-11 13:49)