Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework reset behaviour of ActiveSupport::CurrentAttributes #2774

Closed
18 changes: 18 additions & 0 deletions lib/rspec/rails/adapters.rb
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,24 @@ def method_name
end
end

# @private
#
# Reset ActiveSupport::CurrentAttributes only *after* tests run, rather
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My only concern with this approach is if some code called in a non-Rails example group is setting current attributes, and this would leak, because we reset only for Rails example groups.
Potentially, leak non-empty current attributes into a Rails example group that doesn't expect that.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. That is a shortcoming of the approach used in 6.1.3 already, of course, since it only includes the ActiveSupport helper inside RailsExampleGroup.

Do you have a "feel" for where else in the hierarchy we might include this module to achieve a wider area of effect? It can be put just about anywhere, especially with some defined? and/or respond_to? guards, and should be pretty bomb-proof. Once we've figured that out, we can decide if the adapter module's chosen namespace, as a Rails adapter, is correct or if it should also move elsewhere in the code base.

# than before *and* after.
#
# See https://github.com/rspec/rspec-rails/pull/2774 for more details.
#
module ActiveSupportCurrentAttributesAdapter
extend ActiveSupport::Concern

included do
def after_teardown
super
ActiveSupport::CurrentAttributes.reset_all
end
end
end

# @private
module MinitestAssertionAdapter
extend ActiveSupport::Concern
Expand Down
2 changes: 1 addition & 1 deletion lib/rspec/rails/example/rails_example_group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ module RailsExampleGroup
include RSpec::Rails::FixtureSupport
if ::Rails::VERSION::MAJOR >= 7
include RSpec::Rails::TaggedLoggingAdapter
include ActiveSupport::CurrentAttributes::TestHelper
include RSpec::Rails::ActiveSupportCurrentAttributesAdapter
include ActiveSupport::ExecutionContext::TestHelper
end
end
Expand Down
78 changes: 78 additions & 0 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 @@ -56,5 +58,81 @@ class CurrentSample < ActiveSupport::CurrentAttributes
group.run(failure_reporter) ? true : failure_reporter.exceptions
).to be true
end

context 'with suite-level around-example hooks configured', if: ::Rails::VERSION::MAJOR >= 7 do
let(:uniquely_identifiable_metadata) do
{ configured_around_example_hook: true }
end

# rubocop:disable Lint/ConstantDefinitionInBlock
class CurrentAttrsBetweenHooks < ActiveSupport::CurrentAttributes
attribute :request_id
end
# rubocop:enable Lint/ConstantDefinitionInBlock

# This dirties global state, so tests *MUST* remember to use
pirj marked this conversation as resolved.
Show resolved Hide resolved
# "in_sub_process".
#
before :each do
pond marked this conversation as resolved.
Show resolved Hide resolved

# Client code might legitimately want to wrap examples to ensure
# all-conditions tidy-up, e.g. "ActsAsTenant.without_tenant do...",
# wherein an "around" hook is the only available solution, often used
# in the overall suite via "config.around". Tests would not expect
# anything set in CurrentAttributes here to suddenly be reset by the
# time their actual tests, or their test hooks ran.
#
RSpec.configure do | config |
config.around(:each, uniquely_identifiable_metadata) do | example |
pond marked this conversation as resolved.
Show resolved Hide resolved
CurrentAttrsBetweenHooks.request_id = '123'
example.run
end
end
end

it 'does not reset ActiveSupport::CurrentAttributes before examples' do
pond marked this conversation as resolved.
Show resolved Hide resolved
in_sub_process do
pond marked this conversation as resolved.
Show resolved Hide resolved
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

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
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

expect(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cosmetic

Suggested change
expect(
expect(group.run(failure_reporter)).to be(true), failure_reporter.exceptions

The second arg sent to to is a custom failure message.

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