Skip to content

Commit 9229477

Browse files
committed
Add new RSpec/IncludeExamples cop to prefer it_behaves_like over include_examples
1 parent 4107035 commit 9229477

File tree

8 files changed

+123
-0
lines changed

8 files changed

+123
-0
lines changed

.rubocop.yml

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

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

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

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

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

+1
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'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe RuboCop::Cop::RSpec::IncludeExamples, :config do
4+
it 'registers an offense and corrects for `include_examples`' do
5+
expect_offense(<<~RUBY)
6+
include_examples 'examples'
7+
^^^^^^^^^^^^^^^^ Prefer `it_behaves_like` over `include_examples`.
8+
RUBY
9+
10+
expect_correction(<<~RUBY)
11+
it_behaves_like 'examples'
12+
RUBY
13+
end
14+
15+
it 'does not register an offense for `it_behaves_like`' do
16+
expect_no_offenses(<<~RUBY)
17+
it_behaves_like 'examples'
18+
RUBY
19+
end
20+
21+
it 'does not register an offense for `it_should_behave_like`' do
22+
expect_no_offenses(<<~RUBY)
23+
it_should_behave_like 'examples'
24+
RUBY
25+
end
26+
27+
it 'does not register an offense for `include_context`' do
28+
expect_no_offenses(<<~RUBY)
29+
include_context 'context'
30+
RUBY
31+
end
32+
end

0 commit comments

Comments
 (0)