File tree Expand file tree Collapse file tree 6 files changed +99
-1
lines changed
Expand file tree Collapse file tree 6 files changed +99
-1
lines changed Original file line number Diff line number Diff line change 33## Master (Unreleased)
44
55- Fix a false positive for ` RSpec/LeakyLocalVariable ` when variables are used only in example metadata (e.g., skip messages). ([ @ydah ] )
6+ - Fix a false positive for ` RSpec/ScatteredSetup ` when the hook is defined inside a class method. ([ @d4rky-pl ] )
67
78## 3.8.0 (2025-11-12)
89
@@ -984,6 +985,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
984985[ @composerinteralia ] : https://github.com/composerinteralia
985986[ @corsonknowles ] : https://github.com/corsonknowles
986987[ @corydiamand ] : https://github.com/corydiamand
988+ [ @d4rky-pl ] : https://github.com/d4rky-pl
987989[ @darhazer ] : https://github.com/Darhazer
988990[ @daveworth ] : https://github.com/daveworth
989991[ @dduugg ] : https://github.com/dduugg
Original file line number Diff line number Diff line change @@ -5747,6 +5747,7 @@ Checks for setup scattered across multiple hooks in an example group.
57475747Unify `before` and `after` hooks when possible.
57485748However, `around` hooks are allowed to be defined multiple times,
57495749as unifying them would typically make the code harder to read.
5750+ Hooks defined in class methods are also ignored.
57505751
57515752[#examples-rspecscatteredsetup]
57525753=== Examples
@@ -5772,6 +5773,14 @@ describe Foo do
57725773 around { |example| before1; example.call; after1 }
57735774 around { |example| before2; example.call; after2 }
57745775end
5776+
5777+ # good
5778+ describe Foo do
5779+ before { setup1 }
5780+ def self.setup
5781+ before { setup2 }
5782+ end
5783+ end
57755784----
57765785
57775786[#references-rspecscatteredsetup]
Original file line number Diff line number Diff line change @@ -8,6 +8,7 @@ module RSpec
88 # Unify `before` and `after` hooks when possible.
99 # However, `around` hooks are allowed to be defined multiple times,
1010 # as unifying them would typically make the code harder to read.
11+ # Hooks defined in class methods are also ignored.
1112 #
1213 # @example
1314 # # bad
@@ -30,6 +31,14 @@ module RSpec
3031 # around { |example| before2; example.call; after2 }
3132 # end
3233 #
34+ # # good
35+ # describe Foo do
36+ # before { setup1 }
37+ # def self.setup
38+ # before { setup2 }
39+ # end
40+ # end
41+ #
3342 class ScatteredSetup < Base
3443 include FinalEndLocation
3544 include RangeHelp
@@ -53,9 +62,10 @@ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
5362
5463 private
5564
56- def repeated_hooks ( node )
65+ def repeated_hooks ( node ) # rubocop:disable Metrics/CyclomaticComplexity
5766 hooks = RuboCop ::RSpec ::ExampleGroup . new ( node )
5867 . hooks
68+ . reject ( &:inside_class_method? )
5969 . select { |hook | hook . knowable_scope? && hook . name != :around }
6070 . group_by { |hook | [ hook . name , hook . scope , hook . metadata ] }
6171 . values
Original file line number Diff line number Diff line change @@ -25,6 +25,14 @@ def example?
2525 scope . equal? ( :each )
2626 end
2727
28+ def inside_class_method?
29+ parent = node . parent
30+ return false unless parent
31+
32+ parent . defs_type? ||
33+ ( parent . def_type? && !!parent . parent &.sclass_type? )
34+ end
35+
2836 def scope
2937 return :each if scope_argument &.hash_type?
3038
Original file line number Diff line number Diff line change 202202 end
203203 RUBY
204204 end
205+
206+ it 'ignores blocks defined inside class methods' do
207+ expect_no_offenses ( <<~RUBY )
208+ describe Foo do
209+ before { bar }
210+ def self.setup
211+ before { baz }
212+ end
213+ setup
214+ end
215+ RUBY
216+ end
217+
218+ it 'ignores blocks defined in class << self' do
219+ expect_no_offenses ( <<~RUBY )
220+ describe Foo do
221+ before { bar }
222+ class << self
223+ def setup
224+ before { baz }
225+ end
226+ end
227+ end
228+ RUBY
229+ end
205230end
Original file line number Diff line number Diff line change @@ -137,4 +137,48 @@ def metadata(source)
137137 ) . to eq ( "{#{ expected_symbol } , #{ expected_special } , #{ expected_focus } }" )
138138 end
139139 end
140+
141+ describe '#inside_class_method?' do
142+ def example_group_hook ( source )
143+ RuboCop ::RSpec ::ExampleGroup . new ( parse_source ( source ) . ast ) . hooks . first
144+ end
145+
146+ it 'returns true if the hook is inside class method' do
147+ expect (
148+ example_group_hook (
149+ 'describe Foo { def self.setup; before { do_something }; end }'
150+ ) . inside_class_method?
151+ ) . to be ( true )
152+ end
153+
154+ it 'returns false if the hook is inside instance method' do
155+ expect (
156+ example_group_hook (
157+ 'describe Foo { def setup; before { do_something }; end }'
158+ ) . inside_class_method?
159+ ) . to be ( false )
160+ end
161+
162+ it 'returns false if the hook is in instance method in global scope' do
163+ expect (
164+ example_group_hook (
165+ 'def setup; before { do_something }; end'
166+ ) . inside_class_method?
167+ ) . to be ( false )
168+ end
169+
170+ it 'returns false if the hook is in example group body' do
171+ expect (
172+ example_group_hook (
173+ 'describe Foo { before { do_something } }'
174+ ) . inside_class_method?
175+ ) . to be ( false )
176+ end
177+
178+ it 'returns false if the hook is in global scope' do
179+ expect (
180+ hook ( 'before { do_something }' ) . inside_class_method?
181+ ) . to be ( false )
182+ end
183+ end
140184end
You can’t perform that action at this time.
0 commit comments