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

Commit c8dc2ed

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 54b9339 commit c8dc2ed

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: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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+
unless RSpec.current_scope == :suite
10+
raise "bad current scope: #{RSpec.current_scope.inspect}"
11+
end
12+
13+
at_exit do
14+
exit(1) unless RSpec.current_scope == :suite
15+
end
16+
17+
RSpec.configure do |c|
18+
c.before :suite do
19+
expect(RSpec.current_scope).to eq(:before_suite_hook)
20+
end
21+
22+
c.before :context do
23+
expect(RSpec.current_scope).to eq(:before_context_hook)
24+
end
25+
26+
c.before :example do
27+
expect(RSpec.current_scope).to eq(:before_example_hook)
28+
end
29+
30+
c.around :example do |ex|
31+
expect(RSpec.current_scope).to eq(:before_example_hook)
32+
ex.run
33+
expect(RSpec.current_scope).to eq(:after_example_hook)
34+
end
35+
36+
c.after :example do
37+
expect(RSpec.current_scope).to eq(:after_example_hook)
38+
end
39+
40+
c.after :context do
41+
expect(RSpec.current_scope).to eq(:after_context_hook)
42+
end
43+
44+
c.after :suite do
45+
expect(RSpec.current_scope).to eq(:after_suite_hook)
46+
end
47+
end
48+
49+
RSpec.describe "RSpec.current_scope" do
50+
before :context do
51+
expect(RSpec.current_scope).to eq(:before_context_hook)
52+
end
53+
54+
before :example do
55+
expect(RSpec.current_scope).to eq(:before_example_hook)
56+
end
57+
58+
around :example do |ex|
59+
expect(RSpec.current_scope).to eq(:before_example_hook)
60+
ex.run
61+
expect(RSpec.current_scope).to eq(:after_example_hook)
62+
end
63+
64+
after :example do
65+
expect(RSpec.current_scope).to eq(:after_example_hook)
66+
end
67+
68+
after :context do
69+
expect(RSpec.current_scope).to eq(:after_context_hook)
70+
end
71+
72+
it "is :example in an example" do
73+
expect(RSpec.current_scope).to eq(:example)
74+
end
75+
76+
it "works for multiple examples" do
77+
expect(RSpec.current_scope).to eq(:example)
78+
end
79+
80+
describe "in nested describe blocks" do
81+
it "still works" do
82+
expect(RSpec.current_scope).to eq(:example)
83+
end
84+
end
85+
end
86+
"""
87+
When I run `rspec current_scope_spec.rb`
88+
Then the examples should all pass

lib/rspec/core.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,31 @@ 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 `:suite` while loading your tests (as soon as RSpec has loaded)
142+
#
143+
# Returns `:before_example_hook`/`:before_context_hook`/`:before_suite_hook`/
144+
# `:after_example_hook`/`:after_context_hook`/`:after_suite_hook` in the respective hooks
145+
#
146+
# Returns `:before_example_hook`/`:after_example_hook` inside an `around :each` hook,
147+
# before and after you call `example.run` respectively.
148+
#
149+
# Returns `:example` inside it/example blocks
150+
#
151+
# Returns `:suite` again after your suite and all hooks are done
152+
# @return [Symbol]
153+
def self.current_scope
154+
RSpec::Support.thread_local_data[:current_scope]
155+
end
156+
132157
# @private
133158
# Internal container for global non-configuration data.
134159
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)