Skip to content

Commit fcbb360

Browse files
authored
Merge pull request #2066 from rubocop/include-examples
Add new `RSpec/IncludeExamples` cop to prefer `it_behaves_like` over `include_examples`
2 parents 4107035 + 8d05f42 commit fcbb360

17 files changed

+169
-46
lines changed

.rubocop.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,3 +289,7 @@ Performance/StringIdentifierArgument: {Enabled: true}
289289
Performance/StringInclude: {Enabled: true}
290290
Performance/Sum: {Enabled: true}
291291
Performance/ZipWithoutBlock: {Enabled: true}
292+
293+
# Enable our own pending cops.
294+
295+
RSpec/IncludeExamples: {Enabled: true}

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
- Fix false positive in `RSpec/Pending`, where it would mark the default block `it` as an offense. ([@bquorning])
66
- Fix issue when `Style/ContextWording` is configured with a Prefix being interpreted as a boolean, like `on`. ([@sakuro])
7+
- Add new `RSpec/IncludeExamples` cop to enforce using `it_behaves_like` over `include_examples`. ([@dvandersluis])
78

89
## 3.5.0 (2025-02-16)
910

config/default.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,12 @@ RSpec/ImplicitSubject:
532532
VersionChanged: '2.13'
533533
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ImplicitSubject
534534

535+
RSpec/IncludeExamples:
536+
Description: Checks for usage of `include_examples`.
537+
Enabled: pending
538+
VersionAdded: "<<next>>"
539+
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/IncludeExamples
540+
535541
RSpec/IndexedLet:
536542
Description: Do not set up test data using indexes (e.g., `item_1`, `item_2`).
537543
Enabled: true

docs/modules/ROOT/pages/cops.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
* xref:cops_rspec.adoc#rspecimplicitblockexpectation[RSpec/ImplicitBlockExpectation]
5151
* xref:cops_rspec.adoc#rspecimplicitexpect[RSpec/ImplicitExpect]
5252
* xref:cops_rspec.adoc#rspecimplicitsubject[RSpec/ImplicitSubject]
53+
* xref:cops_rspec.adoc#rspecincludeexamples[RSpec/IncludeExamples]
5354
* xref:cops_rspec.adoc#rspecindexedlet[RSpec/IndexedLet]
5455
* xref:cops_rspec.adoc#rspecinstancespy[RSpec/InstanceSpy]
5556
* xref:cops_rspec.adoc#rspecinstancevariable[RSpec/InstanceVariable]

docs/modules/ROOT/pages/cops_rspec.adoc

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2743,6 +2743,45 @@ it { expect(named_subject).to be_truthy }
27432743
27442744
* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ImplicitSubject
27452745
2746+
[#rspecincludeexamples]
2747+
== RSpec/IncludeExamples
2748+
2749+
|===
2750+
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed
2751+
2752+
| Pending
2753+
| Yes
2754+
| Always
2755+
| <<next>>
2756+
| -
2757+
|===
2758+
2759+
Checks for usage of `include_examples`.
2760+
2761+
`include_examples`, unlike `it_behaves_like`, does not create its
2762+
own context. As such, using `subject`, `let`, `before`/`after`, etc.
2763+
within shared examples included with `include_examples` can have
2764+
unexpected behavior and side effects.
2765+
2766+
Prefer using `it_behaves_like` instead.
2767+
2768+
[#examples-rspecincludeexamples]
2769+
=== Examples
2770+
2771+
[source,ruby]
2772+
----
2773+
# bad
2774+
include_examples 'examples'
2775+
2776+
# good
2777+
it_behaves_like 'examples'
2778+
----
2779+
2780+
[#references-rspecincludeexamples]
2781+
=== References
2782+
2783+
* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/IncludeExamples
2784+
27462785
[#rspecindexedlet]
27472786
== RSpec/IndexedLet
27482787
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# frozen_string_literal: true
2+
3+
module RuboCop
4+
module Cop
5+
module RSpec
6+
# Checks for usage of `include_examples`.
7+
#
8+
# `include_examples`, unlike `it_behaves_like`, does not create its
9+
# own context. As such, using `subject`, `let`, `before`/`after`, etc.
10+
# within shared examples included with `include_examples` can have
11+
# unexpected behavior and side effects.
12+
#
13+
# Prefer using `it_behaves_like` instead.
14+
#
15+
# @example
16+
# # bad
17+
# include_examples 'examples'
18+
#
19+
# # good
20+
# it_behaves_like 'examples'
21+
#
22+
class IncludeExamples < Base
23+
extend AutoCorrector
24+
25+
MSG = 'Prefer `it_behaves_like` over `include_examples`.'
26+
27+
RESTRICT_ON_SEND = %i[include_examples].freeze
28+
29+
def on_send(node)
30+
selector = node.loc.selector
31+
32+
add_offense(selector) do |corrector|
33+
corrector.replace(selector, 'it_behaves_like')
34+
end
35+
end
36+
end
37+
end
38+
end
39+
end

lib/rubocop/cop/rspec_cops.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
require_relative 'rspec/implicit_block_expectation'
4949
require_relative 'rspec/implicit_expect'
5050
require_relative 'rspec/implicit_subject'
51+
require_relative 'rspec/include_examples'
5152
require_relative 'rspec/indexed_let'
5253
require_relative 'rspec/instance_spy'
5354
require_relative 'rspec/instance_variable'

spec/rubocop/cop/rspec/base_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
113113
RUBY
114114
end
115115

116-
include_examples 'it detects `describe`'
116+
it_behaves_like 'it detects `describe`'
117117
end
118118

119119
context 'when `epic` is set as an alias to example group' do
@@ -133,7 +133,7 @@ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
133133
RUBY
134134
end
135135

136-
include_examples 'it detects `describe`'
136+
it_behaves_like 'it detects `describe`'
137137
end
138138
end
139139
end

spec/rubocop/cop/rspec/empty_line_after_hook_spec.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -310,8 +310,8 @@
310310
end
311311

312312
context 'when AllowConsecutiveOneLiners option has default value `true`' do
313-
include_examples 'always require empty line after hook groups'
314-
include_examples 'never allows consecutive multiline blocks'
313+
it_behaves_like 'always require empty line after hook groups'
314+
it_behaves_like 'never allows consecutive multiline blocks'
315315

316316
it 'allows multiple one-liner blocks' do
317317
expect_offense(<<~RUBY)
@@ -381,8 +381,8 @@
381381
context 'when AllowConsecutiveOneLiners option `false`' do
382382
let(:cop_config) { { 'AllowConsecutiveOneLiners' => false } }
383383

384-
include_examples 'always require empty line after hook groups'
385-
include_examples 'never allows consecutive multiline blocks'
384+
it_behaves_like 'always require empty line after hook groups'
385+
it_behaves_like 'never allows consecutive multiline blocks'
386386

387387
it 'registers an offense for multiple one-liner same hook blocks' do
388388
expect_offense(<<~RUBY)

spec/rubocop/cop/rspec/hook_argument_spec.rb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@
2929
end
3030

3131
shared_examples 'an example hook' do
32-
include_examples 'ignored hooks'
33-
include_examples 'detects style', 'before(:each) { foo }', 'each'
34-
include_examples 'detects style', 'before(:example) { foo }', 'example'
35-
include_examples 'detects style', 'before { foo }', 'implicit'
32+
it_behaves_like 'ignored hooks'
33+
it_behaves_like 'detects style', 'before(:each) { foo }', 'each'
34+
it_behaves_like 'detects style', 'before(:example) { foo }', 'example'
35+
it_behaves_like 'detects style', 'before { foo }', 'implicit'
3636
end
3737

3838
context 'when EnforcedStyle is :implicit' do
@@ -87,7 +87,7 @@
8787
RUBY
8888
end
8989

90-
include_examples 'an example hook'
90+
it_behaves_like 'an example hook'
9191

9292
context 'when Ruby 2.7', :ruby27 do
9393
it 'detects :each for hooks' do
@@ -172,7 +172,7 @@
172172
RUBY
173173
end
174174

175-
include_examples 'an example hook'
175+
it_behaves_like 'an example hook'
176176

177177
context 'when Ruby 2.7', :ruby27 do
178178
it 'does not flag :each for hooks' do
@@ -257,7 +257,7 @@
257257
RUBY
258258
end
259259

260-
include_examples 'an example hook'
260+
it_behaves_like 'an example hook'
261261

262262
context 'when Ruby 2.7', :ruby27 do
263263
it 'does not flag :example for hooks' do

0 commit comments

Comments
 (0)