Skip to content

Commit

Permalink
Handle MockExpectationError in aggregate_failures (#1392)
Browse files Browse the repository at this point in the history
When using mocks with aggregated_failures the internal failure list
can leak out and be modified, prevent that by returning a magic value.
  • Loading branch information
mrzasa authored and JonRowe committed Dec 16, 2022
1 parent 635b880 commit d7b2b59
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 0 deletions.
17 changes: 17 additions & 0 deletions lib/rspec/expectations/failure_aggregator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@ module Expectations
class FailureAggregator
attr_reader :block_label, :metadata

# @private
class AggregatedFailure
# @private
MESSAGE =
'AggregatedFailure: This method caused a failure which has been ' \
'supressed to be aggregated into our failure report by returning ' \
'this value, further errors can be ignored.'

def inspect
MESSAGE
end
end

AGGREGATED_FAILURE = AggregatedFailure.new

def aggregate
RSpec::Support.with_failure_notifier(self) do
begin
Expand Down Expand Up @@ -48,6 +63,8 @@ def call(failure, options)
@seen_source_ids[source_id] = true
assign_backtrace(failure) unless failure.backtrace
failures << failure

AGGREGATED_FAILURE
end

private
Expand Down
25 changes: 25 additions & 0 deletions spec/rspec/expectations/failure_aggregator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,31 @@ def expect_error_included_in_aggregated_failure(error)
end
end

describe 'with MockExpectationError' do
it 'does not allow modifications of the failure list in the test example' do
expect {
aggregate_failures do
dbl = double
array = dbl.get(2)
array << :foo
end
}.to raise_error(
be_a(MultipleExpectationsNotMetError).and have_attributes(
:failures => [
be_a(RSpec::Mocks::MockExpectationError)
],
:other_errors => [
# Checking only the class name, not the full message, because the hehaviour differes bettern Ruby 2 and 3.
# Ruby 3 uses #inspect in NoMethodError, while Ruby 2.x uses format that I can't override:
# NoMethodError (undefined method `<<' for #<RSpec::Expectations::FailureAggregator::AggregatedFailure:0x000055e4bac69ba8>)
# even if I override #to_s
be_a(NoMethodError).and(have_attributes(:message => /AggregatedFailure/))
]
)
)
end
end

describe "message formatting" do
it "enumerates the failures with an index label, the path of each failure and a blank line in between" do
expect {
Expand Down

0 comments on commit d7b2b59

Please sign in to comment.