Skip to content
This repository was archived by the owner on Nov 30, 2024. It is now read-only.

Commit d7b2b59

Browse files
mrzasaJonRowe
authored andcommitted
Handle MockExpectationError in aggregate_failures (#1392)
When using mocks with aggregated_failures the internal failure list can leak out and be modified, prevent that by returning a magic value.
1 parent 635b880 commit d7b2b59

2 files changed

Lines changed: 42 additions & 0 deletions

File tree

lib/rspec/expectations/failure_aggregator.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,21 @@ module Expectations
44
class FailureAggregator
55
attr_reader :block_label, :metadata
66

7+
# @private
8+
class AggregatedFailure
9+
# @private
10+
MESSAGE =
11+
'AggregatedFailure: This method caused a failure which has been ' \
12+
'supressed to be aggregated into our failure report by returning ' \
13+
'this value, further errors can be ignored.'
14+
15+
def inspect
16+
MESSAGE
17+
end
18+
end
19+
20+
AGGREGATED_FAILURE = AggregatedFailure.new
21+
722
def aggregate
823
RSpec::Support.with_failure_notifier(self) do
924
begin
@@ -48,6 +63,8 @@ def call(failure, options)
4863
@seen_source_ids[source_id] = true
4964
assign_backtrace(failure) unless failure.backtrace
5065
failures << failure
66+
67+
AGGREGATED_FAILURE
5168
end
5269

5370
private

spec/rspec/expectations/failure_aggregator_spec.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,31 @@ def expect_error_included_in_aggregated_failure(error)
265265
end
266266
end
267267

268+
describe 'with MockExpectationError' do
269+
it 'does not allow modifications of the failure list in the test example' do
270+
expect {
271+
aggregate_failures do
272+
dbl = double
273+
array = dbl.get(2)
274+
array << :foo
275+
end
276+
}.to raise_error(
277+
be_a(MultipleExpectationsNotMetError).and have_attributes(
278+
:failures => [
279+
be_a(RSpec::Mocks::MockExpectationError)
280+
],
281+
:other_errors => [
282+
# Checking only the class name, not the full message, because the hehaviour differes bettern Ruby 2 and 3.
283+
# Ruby 3 uses #inspect in NoMethodError, while Ruby 2.x uses format that I can't override:
284+
# NoMethodError (undefined method `<<' for #<RSpec::Expectations::FailureAggregator::AggregatedFailure:0x000055e4bac69ba8>)
285+
# even if I override #to_s
286+
be_a(NoMethodError).and(have_attributes(:message => /AggregatedFailure/))
287+
]
288+
)
289+
)
290+
end
291+
end
292+
268293
describe "message formatting" do
269294
it "enumerates the failures with an index label, the path of each failure and a blank line in between" do
270295
expect {

0 commit comments

Comments
 (0)