Skip to content

Commit

Permalink
Use 'in_sub_process' to properly manage global state mutation
Browse files Browse the repository at this point in the history
  • Loading branch information
pond committed Jul 4, 2024
1 parent beda807 commit c35bb77
Showing 1 changed file with 41 additions and 45 deletions.
86 changes: 41 additions & 45 deletions spec/rspec/rails/example/rails_example_group_spec.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require 'rspec/support/spec/in_sub_process'

module RSpec::Rails
RSpec.describe RailsExampleGroup do
it 'supports tagged_logger', if: ::Rails::VERSION::MAJOR >= 7 do
Expand Down Expand Up @@ -68,13 +70,10 @@ class CurrentAttrsBetweenHooks < ActiveSupport::CurrentAttributes
end
# rubocop:enable Lint/ConstantDefinitionInBlock

# We have to modify the suite's around-each in RSpec.config, but don't
# want to pollute other tests with this (whether or not it is harmless
# to do so). There being no public API to read or remove hooks, instead
# it's necessary use some private APIs to be able to delete the added
# hook via 'ensure'.
# This dirties global state, so tests *MUST* remember to use
# "in_sub_process".
#
around :each do | outer_example |
before :each do

# Client code might legitimately want to wrap examples to ensure
# all-conditions tidy-up, e.g. "ActsAsTenant.without_tenant do...",
Expand All @@ -84,58 +83,55 @@ class CurrentAttrsBetweenHooks < ActiveSupport::CurrentAttributes
# time their actual tests, or their test hooks ran.
#
RSpec.configure do | config |
config.around(:each, uniquely_identifiable_metadata) do | inner_example |
config.around(:each, uniquely_identifiable_metadata) do | example |
CurrentAttrsBetweenHooks.request_id = '123'
inner_example.run
example.run
end
end

outer_example.run

ensure
around_example_repository = RSpec.configuration.hooks.send(:hooks_for, :around, :example)
item_we_added = around_example_repository.items_for(uniquely_identifiable_metadata).first
around_example_repository.delete(item_we_added, uniquely_identifiable_metadata)
end

it 'does not reset ActiveSupport::CurrentAttributes before examples' do
group =
RSpec::Core::ExampleGroup.describe('A group', uniquely_identifiable_metadata) do
include RSpec::Rails::RailsExampleGroup

it 'runs normally' do
expect(CurrentAttrsBetweenHooks.request_id).to eq('123')
in_sub_process do
group =
RSpec::Core::ExampleGroup.describe('A group', uniquely_identifiable_metadata) do
include RSpec::Rails::RailsExampleGroup

it 'runs normally' do
expect(CurrentAttrsBetweenHooks.request_id).to eq('123')
end
end
end

expect(
group.run(failure_reporter) ? true : failure_reporter.exceptions
).to be true
expect(
group.run(failure_reporter) ? true : failure_reporter.exceptions
).to be true
end
end

it 'does not reset ActiveSupport::CurrentAttributes before before-each hooks' do
group =
RSpec::Core::ExampleGroup.describe('A group', uniquely_identifiable_metadata) do
include RSpec::Rails::RailsExampleGroup

# Client code will often have test setup blocks within "*_spec.rb"
# files that might set up data or other environmental factors for a
# group of tests in e.g. a "before" hook, but would reasonably expect
# suite-wide 'around' settings to remain intact and not be reset.
#
before :each do
expect(CurrentAttrsBetweenHooks.request_id).to eq('123')
CurrentAttrsBetweenHooks.request_id = '234'
in_sub_process do
group =
RSpec::Core::ExampleGroup.describe('A group', uniquely_identifiable_metadata) do
include RSpec::Rails::RailsExampleGroup

# Client code will often have test setup blocks within "*_spec.rb"
# files that might set up data or other environmental factors for a
# group of tests in e.g. a "before" hook, but would reasonably expect
# suite-wide 'around' settings to remain intact and not be reset.
#
before :each do
expect(CurrentAttrsBetweenHooks.request_id).to eq('123')
CurrentAttrsBetweenHooks.request_id = '234'
end

it 'runs normally' do
expect(CurrentAttrsBetweenHooks.request_id).to eq('234')
end
end

it 'runs normally' do
expect(CurrentAttrsBetweenHooks.request_id).to eq('234')
end
end

expect(
group.run(failure_reporter) ? true : failure_reporter.exceptions
).to be true
expect(
group.run(failure_reporter) ? true : failure_reporter.exceptions
).to be true
end
end
end
end
Expand Down

0 comments on commit c35bb77

Please sign in to comment.