Skip to content

Commit ea7a5d8

Browse files
authored
Merge pull request #1893 from krororo/add-expectation_target_method
Add new `RSpec/MissingExpectationTargetMethod` cop
2 parents ef447c1 + 87e0e4a commit ea7a5d8

File tree

8 files changed

+183
-0
lines changed

8 files changed

+183
-0
lines changed

.rubocop.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,8 @@ RSpec/MatchArray:
288288
Enabled: true
289289
RSpec/MetadataStyle:
290290
Enabled: true
291+
RSpec/MissingExpectationTargetMethod:
292+
Enabled: true
291293
RSpec/NoExpectationExample:
292294
Enabled: true
293295
RSpec/PendingWithoutReason:

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- Remove `RSpec/Capybara/FeatureMethods` cop. If you are using this cop, change it to use `RSpec/Dialect`. ([@ydah])
1010
- Support `AutoCorrect: contextual` option for LSP. ([@ydah])
1111
- Enable all pending cops. ([@bquorning])
12+
- Add new `RSpec/MissingExpectationTargetMethod` cop. ([@krororo])
1213

1314
## 2.29.2 (2024-05-02)
1415

@@ -924,6 +925,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
924925
[@k-s-a]: https://github.com/K-S-A
925926
[@kellysutton]: https://github.com/kellysutton
926927
[@koic]: https://github.com/koic
928+
[@krororo]: https://github.com/krororo
927929
[@kuahyeow]: https://github.com/kuahyeow
928930
[@lazycoder9]: https://github.com/lazycoder9
929931
[@leoarnold]: https://github.com/leoarnold

config/default.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,12 @@ RSpec/MissingExampleGroupArgument:
657657
VersionAdded: '1.28'
658658
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MissingExampleGroupArgument
659659

660+
RSpec/MissingExpectationTargetMethod:
661+
Description: Checks if `.to`, `not_to` or `to_not` are used.
662+
Enabled: pending
663+
VersionAdded: "<<next>>"
664+
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MissingExpectationTargetMethod
665+
660666
RSpec/MultipleDescribes:
661667
Description: Checks for multiple top-level example groups.
662668
Enabled: true

docs/modules/ROOT/pages/cops.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
* xref:cops_rspec.adoc#rspecmessagespies[RSpec/MessageSpies]
6767
* xref:cops_rspec.adoc#rspecmetadatastyle[RSpec/MetadataStyle]
6868
* xref:cops_rspec.adoc#rspecmissingexamplegroupargument[RSpec/MissingExampleGroupArgument]
69+
* xref:cops_rspec.adoc#rspecmissingexpectationtargetmethod[RSpec/MissingExpectationTargetMethod]
6970
* xref:cops_rspec.adoc#rspecmultipledescribes[RSpec/MultipleDescribes]
7071
* xref:cops_rspec.adoc#rspecmultipleexpectations[RSpec/MultipleExpectations]
7172
* xref:cops_rspec.adoc#rspecmultiplememoizedhelpers[RSpec/MultipleMemoizedHelpers]

docs/modules/ROOT/pages/cops_rspec.adoc

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3406,6 +3406,42 @@ end
34063406
34073407
* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MissingExampleGroupArgument
34083408
3409+
== RSpec/MissingExpectationTargetMethod
3410+
3411+
|===
3412+
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed
3413+
3414+
| Pending
3415+
| Yes
3416+
| No
3417+
| <<next>>
3418+
| -
3419+
|===
3420+
3421+
Checks if `.to`, `not_to` or `to_not` are used.
3422+
3423+
The RSpec::Expectations::ExpectationTarget must use `to`, `not_to` or
3424+
`to_not` to run. Therefore, this cop checks if other methods are used.
3425+
3426+
=== Examples
3427+
3428+
[source,ruby]
3429+
----
3430+
# bad
3431+
expect(something).kind_of? Foo
3432+
is_expected == 42
3433+
expect{something}.eq? BarError
3434+
3435+
# good
3436+
expect(something).to be_a Foo
3437+
is_expected.to eq 42
3438+
expect{something}.to raise_error BarError
3439+
----
3440+
3441+
=== References
3442+
3443+
* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MissingExpectationTargetMethod
3444+
34093445
== RSpec/MultipleDescribes
34103446
34113447
|===
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# frozen_string_literal: true
2+
3+
module RuboCop
4+
module Cop
5+
module RSpec
6+
# Checks if `.to`, `not_to` or `to_not` are used.
7+
#
8+
# The RSpec::Expectations::ExpectationTarget must use `to`, `not_to` or
9+
# `to_not` to run. Therefore, this cop checks if other methods are used.
10+
#
11+
# @example
12+
# # bad
13+
# expect(something).kind_of? Foo
14+
# is_expected == 42
15+
# expect{something}.eq? BarError
16+
#
17+
# # good
18+
# expect(something).to be_a Foo
19+
# is_expected.to eq 42
20+
# expect{something}.to raise_error BarError
21+
#
22+
class MissingExpectationTargetMethod < Base
23+
MSG = 'Use `.to`, `.not_to` or `.to_not` to set an expectation.'
24+
RESTRICT_ON_SEND = %i[expect is_expected].freeze
25+
26+
# @!method expect?(node)
27+
def_node_matcher :expect?, <<~PATTERN
28+
{
29+
(send nil? :expect ...)
30+
(send nil? :is_expected)
31+
}
32+
PATTERN
33+
34+
# @!method expect_block?(node)
35+
def_node_matcher :expect_block?, <<~PATTERN
36+
(block #expect? (args) _body)
37+
PATTERN
38+
39+
# @!method expectation_without_runner?(node)
40+
def_node_matcher :expectation_without_runner?, <<~PATTERN
41+
(send {#expect? #expect_block?} !#Runners.all ...)
42+
PATTERN
43+
44+
def on_send(node)
45+
node = node.parent if node.parent&.block_type?
46+
47+
expectation_without_runner?(node.parent) do
48+
add_offense(node.parent.loc.selector)
49+
end
50+
end
51+
end
52+
end
53+
end
54+
end

lib/rubocop/cop/rspec_cops.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
require_relative 'rspec/message_spies'
6565
require_relative 'rspec/metadata_style'
6666
require_relative 'rspec/missing_example_group_argument'
67+
require_relative 'rspec/missing_expectation_target_method'
6768
require_relative 'rspec/multiple_describes'
6869
require_relative 'rspec/multiple_expectations'
6970
require_relative 'rspec/multiple_memoized_helpers'
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe RuboCop::Cop::RSpec::MissingExpectationTargetMethod do
4+
it 'registers offenses to other than `to` or `not_to`' do
5+
expect_offense(<<~RUBY)
6+
it 'something' do
7+
something = 1
8+
expect(something).kind_of? Integer
9+
^^^^^^^^ Use `.to`, `.not_to` or `.to_not` to set an expectation.
10+
end
11+
RUBY
12+
end
13+
14+
it 'registers offenses to other than `to` or `not_to` ' \
15+
'when block has one expression' do
16+
expect_offense(<<~RUBY)
17+
it 'something' do
18+
expect(something).eq? 42
19+
^^^ Use `.to`, `.not_to` or `.to_not` to set an expectation.
20+
end
21+
RUBY
22+
end
23+
24+
it 'registers offenses to other than `to` or `not_to` with `is_expected`' do
25+
expect_offense(<<~RUBY)
26+
it 'something' do
27+
is_expected == 42
28+
^^ Use `.to`, `.not_to` or `.to_not` to set an expectation.
29+
end
30+
RUBY
31+
end
32+
33+
it 'registers offenses to other than `to` or `not_to` with block' do
34+
expect_offense(<<~RUBY)
35+
it 'something' do
36+
expect{something}.kind_of? StandardError
37+
^^^^^^^^ Use `.to`, `.not_to` or `.to_not` to set an expectation.
38+
end
39+
RUBY
40+
end
41+
42+
it 'accepts void `expect`' do
43+
expect_no_offenses(<<~RUBY)
44+
it 'something' do
45+
expect(something)
46+
end
47+
RUBY
48+
end
49+
50+
it 'accepts only `expect`' do
51+
expect_no_offenses(<<~RUBY)
52+
expect
53+
RUBY
54+
end
55+
56+
%w[to not_to to_not].each do |method|
57+
it "accepts `.#{method}`" do
58+
expect_no_offenses(<<~RUBY)
59+
it 'something' do
60+
expect(something).#{method} be_a Integer
61+
end
62+
RUBY
63+
end
64+
65+
it "accepts `.#{method}` with `is_expected`" do
66+
expect_no_offenses(<<~RUBY)
67+
it 'something' do
68+
is_expected.#{method} eq 42
69+
end
70+
RUBY
71+
end
72+
73+
it "accepts `.#{method}` with block" do
74+
expect_no_offenses(<<~RUBY)
75+
it 'something' do
76+
expect{something}.#{method} raise_error(StandardError)
77+
end
78+
RUBY
79+
end
80+
end
81+
end

0 commit comments

Comments
 (0)