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

Commit e32d1ad

Browse files
odinhbpirj
authored andcommitted
Implement RSpec.current_scope
My reasoning is: rspec-rails needs to do the following condition: `if RSpec.current_scope == :before_context_hook` test_prof needs to do the following condition: `if RSpec.current_scope == :before_context_hook` amd my little helper needs the following `unless [:before_example_hook, :example].include?(RSpec.current_scope)`
1 parent 91b7c0e commit e32d1ad

File tree

8 files changed

+138
-1
lines changed

8 files changed

+138
-1
lines changed

.rubocop.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Metrics/LineLength:
2424

2525
# This should go down over time.
2626
Metrics/MethodLength:
27-
Max: 37
27+
Max: 39
2828

2929
# This should go down over time.
3030
Metrics/CyclomaticComplexity:

Changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ Enhancements:
66
* Improve pluralisation of words ending with `s` (like process). (Joshua Pinter, #2779)
77
* Add ordering by file modification time (most recent first). (Matheus Richard, #2778)
88
* Add `to_s` to reserved names for #let and #subject. (Nick Flückiger, #2886)
9+
* Introduce `RSpec.current_scope` to expose the current scope in which
10+
RSpec is executing. e.g. `:before_example_hook`, `:example` etc. (@odinhb, #2895)
911

1012
Bug fixes:
1113

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
Feature: RSpec provides the current scope as RSpec.current_scope
2+
3+
You can detect which rspec scope your helper methods or library code is executing in.
4+
This is useful if for example, your method only makes sense to call in a certain context.
5+
6+
Scenario: Detecting the current scope
7+
Given a file named "current_scope_spec.rb" with:
8+
"""ruby
9+
# Outside of the test lifecycle, the current scope is `:suite`
10+
exit(1) unless RSpec.current_scope == :suite
11+
12+
at_exit do
13+
exit(1) unless RSpec.current_scope == :suite
14+
end
15+
16+
RSpec.configure do |c|
17+
c.before :suite do
18+
expect(RSpec.current_scope).to eq(:before_suite_hook)
19+
end
20+
21+
c.before :context do
22+
expect(RSpec.current_scope).to eq(:before_context_hook)
23+
end
24+
25+
c.before :example do
26+
expect(RSpec.current_scope).to eq(:before_example_hook)
27+
end
28+
29+
c.around :example do |ex|
30+
expect(RSpec.current_scope).to eq(:before_example_hook)
31+
ex.run
32+
expect(RSpec.current_scope).to eq(:after_example_hook)
33+
end
34+
35+
c.after :example do
36+
expect(RSpec.current_scope).to eq(:after_example_hook)
37+
end
38+
39+
c.after :context do
40+
expect(RSpec.current_scope).to eq(:after_context_hook)
41+
end
42+
43+
c.after :suite do
44+
expect(RSpec.current_scope).to eq(:after_suite_hook)
45+
end
46+
end
47+
48+
RSpec.describe "RSpec.current_scope" do
49+
before :context do
50+
expect(RSpec.current_scope).to eq(:before_context_hook)
51+
end
52+
53+
before :example do
54+
expect(RSpec.current_scope).to eq(:before_example_hook)
55+
end
56+
57+
around :example do |ex|
58+
expect(RSpec.current_scope).to eq(:before_example_hook)
59+
ex.run
60+
expect(RSpec.current_scope).to eq(:after_example_hook)
61+
end
62+
63+
after :example do
64+
expect(RSpec.current_scope).to eq(:after_example_hook)
65+
end
66+
67+
after :context do
68+
expect(RSpec.current_scope).to eq(:after_context_hook)
69+
end
70+
71+
it "is :example in an example" do
72+
expect(RSpec.current_scope).to eq(:example)
73+
end
74+
75+
it "works for multiple examples" do
76+
expect(RSpec.current_scope).to eq(:example)
77+
end
78+
79+
describe "in nested describe blocks" do
80+
it "still works" do
81+
expect(RSpec.current_scope).to eq(:example)
82+
end
83+
end
84+
end
85+
"""
86+
When I run `rspec current_scope_spec.rb`
87+
Then the examples should all pass

lib/rspec/core.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,32 @@ def self.current_example=(example)
129129
RSpec::Support.thread_local_data[:current_example] = example
130130
end
131131

132+
# Set the current scope rspec is executing in
133+
# @api private
134+
def self.current_scope=(scope)
135+
RSpec::Support.thread_local_data[:current_scope] = scope
136+
end
137+
RSpec.current_scope = :suite
138+
139+
# Get the current RSpec execution scope
140+
#
141+
# Returns (in order of lifecycle):
142+
# * `:suite` as an initial value, this is outside of the test lifecycle.
143+
# * `:before_suite_hook` during `before(:suite)` hooks.
144+
# * `:before_context_hook` during `before(:context)` hooks.
145+
# * `:before_example_hook` during `before(:example)` hooks and `around(:example)` before `example.run`.
146+
# * `:example` within the example run.
147+
# * `:after_example_hook` during `after(:example)` hooks and `around(:example)` after `example.run`.
148+
# * `:after_context_hook` during `after(:context)` hooks.
149+
# * `:after_suite_hook` during `after(:suite)` hooks.
150+
# * `:suite` as a final value, again this is outside of the test lifecycle.
151+
#
152+
# Reminder, `:context` hooks have `:all` alias and `:example` hooks have `:each` alias.
153+
# @return [Symbol]
154+
def self.current_scope
155+
RSpec::Support.thread_local_data[:current_scope]
156+
end
157+
132158
# @private
133159
# Internal container for global non-configuration data.
134160
def self.world

lib/rspec/core/configuration.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2063,10 +2063,13 @@ def with_suite_hooks
20632063
return yield if dry_run?
20642064

20652065
begin
2066+
RSpec.current_scope = :before_suite_hook
20662067
run_suite_hooks("a `before(:suite)` hook", @before_suite_hooks)
20672068
yield
20682069
ensure
2070+
RSpec.current_scope = :after_suite_hook
20692071
run_suite_hooks("an `after(:suite)` hook", @after_suite_hooks)
2072+
RSpec.current_scope = :suite
20702073
end
20712074
end
20722075

lib/rspec/core/example.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ def run(example_group_instance, reporter)
259259
with_around_and_singleton_context_hooks do
260260
begin
261261
run_before_example
262+
RSpec.current_scope = :example
262263
@example_group_instance.instance_exec(self, &@example_block)
263264

264265
if pending?
@@ -278,6 +279,7 @@ def run(example_group_instance, reporter)
278279
rescue AllExceptionsExcludingDangerousOnesOnRubiesThatAllowIt => e
279280
set_exception(e)
280281
ensure
282+
RSpec.current_scope = :after_example_hook
281283
run_after_example
282284
end
283285
end
@@ -462,6 +464,7 @@ def hooks
462464
end
463465

464466
def with_around_example_hooks
467+
RSpec.current_scope = :before_example_hook
465468
hooks.run(:around, :example, self) { yield }
466469
rescue Support::AllExceptionsExceptOnesWeMustNotRescue => e
467470
set_exception(e)

lib/rspec/core/example_group.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,7 @@ def self.run(reporter=RSpec::Core::NullReporter)
608608

609609
should_run_context_hooks = descendant_filtered_examples.any?
610610
begin
611+
RSpec.current_scope = :before_context_hook
611612
run_before_context_hooks(new('before(:context) hook')) if should_run_context_hooks
612613
result_for_this_group = run_examples(reporter)
613614
results_for_descendants = ordering_strategy.order(children).map { |child| child.run(reporter) }.all?
@@ -620,6 +621,7 @@ def self.run(reporter=RSpec::Core::NullReporter)
620621
RSpec.world.wants_to_quit = true if reporter.fail_fast_limit_met?
621622
false
622623
ensure
624+
RSpec.current_scope = :after_context_hook
623625
run_after_context_hooks(new('after(:context) hook')) if should_run_context_hooks
624626
reporter.example_group_finished(self)
625627
end

spec/rspec/core_spec.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,20 @@
129129
end
130130
end
131131

132+
describe ".current_scope" do
133+
before :context do
134+
expect(RSpec.current_scope).to eq(:before_context_hook)
135+
end
136+
137+
before do
138+
expect(RSpec.current_scope).to eq(:before_example_hook)
139+
end
140+
141+
it "returns :example inside an example" do
142+
expect(RSpec.current_scope).to eq(:example)
143+
end
144+
end
145+
132146
describe ".reset" do
133147
it "resets the configuration and world objects" do
134148
config_before_reset = RSpec.configuration

0 commit comments

Comments
 (0)