Read more

RSpec: How to aggregate failures

Jakob Scholz
March 01, 2019Software engineer at makandra GmbH

RSpec >= 3.3 added aggregate_failures Show archive.org snapshot , which allows multiple failures in an example and list them all, rather than aborting on the first failure.

Illustration UI/UX Design

UI/UX Design by makandra brand

We make sure that your target audience has the best possible experience with your digital product. You get:

  • Design tailored to your audience
  • Proven processes customized to your needs
  • An expert team of experienced designers
Read more Show archive.org snapshot

This can be used:

  • In the global configuration
  • With the tag :aggregate_failures (our preferred option in case every expectations should be aggregated)
  • With the method aggregate_failures

Here Show archive.org snapshot you can find several scenarios for all three usage options.

Below is an example, which shows why aggregating failures are useful.

Example

Basic Usage

aggregate_failures do
  first_expectation_object = false
  second_expectation_object = false
  third_expectation_object = true

  expect(first_expectation_object).to be(true), "first expectation failed"   # will fail
  expect(second_expectation_object).to be(true), "second expectation failed" # will fail
  expect(third_expectation_object).to be(true), "third expectation failed"   # will pass
end
(::) failed steps (::)

Got 2 failures from failure aggregation block "multiple expectations":

  1) first expectation failed

  2) second expectation failed

As you can see, the test is not being interrupted by the first two falsy expectations, the third expectation passed without a message.

This can be handy if you are testing for multiple values, which map to one context, e. g. mail components, in order to provide better readability of your tests or to even make clear a tests fail reason due to some dependency:

aggregate_failures "email header" do
  expect(mail.subject).to match("Welcome")
  expect(mail.to).to contain_exactly("user@somedomain.de")
  expect(mail.from).to contain_exactly("welcome@makandra.de")
end
first_recipient = method_that_creates_a_user
aggregate_failures "mail sent" do
  expect(ActionMailer::Base.deliveries.last.to).to contain("user@somedomain.de")
  expect(first_recipient.email).to match("user@somedomain.de")
end

In the last example first_recipient has been created in a different file, so it might not be obvious (in more complicated cases, imagine a list of more recipients) that method_that_creates_a_user actually creates a user with the mail address user@somedomain.com.

For Console Debugging

If you might want to read out the result of at least one failing expectation for different expected values without interrupting the test execution, this can be achieved with aggregated failures:

aggregate_failures do
  expected = expect(page).send(expectation, have_content(text))
  byebug
end

Normally, the breakpoint would be interrupted as soon as the expecation is false. Now you can check the value of expected for different versions of text.

Jakob Scholz
March 01, 2019Software engineer at makandra GmbH
Posted by Jakob Scholz to makandra dev (2019-03-01 14:15)