Skip to content

Include exception that raised failure in backtrace

Created by: mrageh

Problem

Sometimes rspec-core does not display the underlying cause of an exception that was raised.

Previously if an exception was raised there would sometimes be an underlying cause of the exception. For example an exception that's raised and rescued within an application causes the exception that is displayed in the stacktrace.

class BrokenCode
  def self.shallow_method
    begin
      deep_method
    rescue
      raise "Something happened and I'm hiding that from the message
'because usability'."
    end
  end

  def self.deep_method
    raise 'The real cause for the failure is here!'
  end
end

RSpec.describe 'some broken code' do
  it 'works' do
    BrokenCode.shallow_method
  end
end
  1) some broken code works
     Failure/Error: raise "Something happened and I'm hiding that from
the message 'because usability'."
     RuntimeError:
       Something happened and I'm hiding that from the message 'because
usability'.
     # ./my_spec.rb:32:in `rescue in shallow_method'
     # ./my_spec.rb:29:in `shallow_method'
     # ./my_spec.rb:43:in `block (2 levels) in <top (required)>'

The above example demonstrates the problem, the stacktrace above is not very clear as it does not show the initial exception that caused the problem. This makes it difficult to fix their failing test.

Solution

Ruby 2.1 introduced Exception#cause which shows the underlying cause of an exception if there is one. This commit uses that new Ruby method to get the cause of an exception and display it in the stacktrace.

The stacktrace for the above code example would change to the below example:

  1) some broken code works
     Failure/Error: raise "Something happened and I'm hiding that from
the message 'because usability'."
     RuntimeError:
       Something happened and I'm hiding that from the message 'because
usability'.
     # ./my_spec.rb:32:in `rescue in shallow_method'
     # ./my_spec.rb:29:in `shallow_method'
     # ./my_spec.rb:43:in `block (2 levels) in <top (required)>'
  ------------------
  --- Caused by: ---
     RuntimeError:
       The real cause for the failure is here!
     # ./my_spec.rb:37:in `deep_method'
     # ./my_spec.rb:30:in `shallow_method'
     # ./my_spec.rb:43:in `block (2 levels) in <top (required)>'

This makes it a lot easier to fix the main cause of the test failure.

Edge case

There may be a situation where lots of exceptions are captured and bubble up the stacktrace. In that case I don't think we want to print out the cause of every single exception. Instead what I propose we do is to print out the first exception that caused the other exceptions to bubble up the stack trace.

This may be a potential fix for #2044 (closed)

Merge request reports