Skip to content

Commit 1ec40e3

Browse files
Thomas Noepirj
authored andcommitted
Add VerifiedDoubleReference cop
1 parent 072ffe9 commit 1ec40e3

File tree

7 files changed

+282
-0
lines changed

7 files changed

+282
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Changelog
22

33
## Master (Unreleased)
4+
* Add `RSpec/VerifiedDoubleReference` cop. ([@t3h2mas][])
45

56
* Fix a false positive for `RSpec/EmptyExampleGroup` when expectations in case statement. ([@ydah][])
67

@@ -675,3 +676,4 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
675676
[@harry-graham]: https://github.com/harry-graham
676677
[@oshiro3]: https://github.com/oshiro3
677678
[@ydah]: https://github.com/ydah
679+
[@t3h2mas]: https://github.com/t3h2mas

config/default.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,16 @@ RSpec/VariableName:
761761
VersionChanged: '1.43'
762762
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/VariableName
763763

764+
RSpec/VerifiedDoubleReference:
765+
Description: Checks for consistent verified double reference style.
766+
Enabled: pending
767+
EnforcedStyle: constant
768+
SupportedStyles:
769+
- constant
770+
- string
771+
VersionAdded: 2.10.0
772+
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/VerifiedDoubleReference
773+
764774
RSpec/VerifiedDoubles:
765775
Description: Prefer using verifying doubles over normal doubles.
766776
Enabled: true

docs/modules/ROOT/pages/cops.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
* xref:cops_rspec.adoc#rspecunspecifiedexception[RSpec/UnspecifiedException]
8484
* xref:cops_rspec.adoc#rspecvariabledefinition[RSpec/VariableDefinition]
8585
* xref:cops_rspec.adoc#rspecvariablename[RSpec/VariableName]
86+
* xref:cops_rspec.adoc#rspecverifieddoublereference[RSpec/VerifiedDoubleReference]
8687
* xref:cops_rspec.adoc#rspecverifieddoubles[RSpec/VerifiedDoubles]
8788
* xref:cops_rspec.adoc#rspecvoidexpect[RSpec/VoidExpect]
8889
* xref:cops_rspec.adoc#rspecyield[RSpec/Yield]

docs/modules/ROOT/pages/cops_rspec.adoc

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4300,6 +4300,81 @@ let(:userFood_2) { 'fettuccine' }
43004300

43014301
* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/VariableName
43024302

4303+
== RSpec/VerifiedDoubleReference
4304+
4305+
|===
4306+
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed
4307+
4308+
| Pending
4309+
| Yes
4310+
| Yes
4311+
| 2.10.0
4312+
| -
4313+
|===
4314+
4315+
Checks for consistent verified double reference style.
4316+
4317+
Only investigates references that are one of the supported styles.
4318+
4319+
This cop can be configured in your configuration using the
4320+
`EnforcedStyle` option and supports `--auto-gen-config`.
4321+
4322+
=== Examples
4323+
4324+
==== `EnforcedStyle: constant` (default)
4325+
4326+
[source,ruby]
4327+
----
4328+
# bad
4329+
let(:foo) do
4330+
instance_double('ClassName', method_name: 'returned_value')
4331+
end
4332+
4333+
# good
4334+
let(:foo) do
4335+
instance_double(ClassName, method_name: 'returned_value')
4336+
end
4337+
----
4338+
4339+
==== `EnforcedStyle: string`
4340+
4341+
[source,ruby]
4342+
----
4343+
# bad
4344+
let(:foo) do
4345+
instance_double(ClassName, method_name: 'returned_value')
4346+
end
4347+
4348+
# good
4349+
let(:foo) do
4350+
instance_double('ClassName', method_name: 'returned_value')
4351+
end
4352+
----
4353+
4354+
==== Reference is not in the supported style list. No enforcement
4355+
4356+
[source,ruby]
4357+
----
4358+
# good
4359+
let(:foo) do
4360+
instance_double(@klass, method_name: 'returned_value')
4361+
end
4362+
----
4363+
4364+
=== Configurable attributes
4365+
4366+
|===
4367+
| Name | Default value | Configurable values
4368+
4369+
| EnforcedStyle
4370+
| `constant`
4371+
| `constant`, `string`
4372+
|===
4373+
4374+
=== References
4375+
4376+
* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/VerifiedDoubleReference
4377+
43034378
== RSpec/VerifiedDoubles
43044379

43054380
|===
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# frozen_string_literal: true
2+
3+
module RuboCop
4+
module Cop
5+
module RSpec
6+
# Checks for consistent verified double reference style.
7+
#
8+
# Only investigates references that are one of the supported styles.
9+
#
10+
# @see https://relishapp.com/rspec/rspec-mocks/docs/verifying-doubles
11+
#
12+
# This cop can be configured in your configuration using the
13+
# `EnforcedStyle` option and supports `--auto-gen-config`.
14+
#
15+
# @example `EnforcedStyle: constant` (default)
16+
# # bad
17+
# let(:foo) do
18+
# instance_double('ClassName', method_name: 'returned_value')
19+
# end
20+
#
21+
# # good
22+
# let(:foo) do
23+
# instance_double(ClassName, method_name: 'returned_value')
24+
# end
25+
#
26+
# @example `EnforcedStyle: string`
27+
# # bad
28+
# let(:foo) do
29+
# instance_double(ClassName, method_name: 'returned_value')
30+
# end
31+
#
32+
# # good
33+
# let(:foo) do
34+
# instance_double('ClassName', method_name: 'returned_value')
35+
# end
36+
#
37+
# @example Reference is not in the supported style list. No enforcement
38+
#
39+
# # good
40+
# let(:foo) do
41+
# instance_double(@klass, method_name: 'returned_value')
42+
# end
43+
class VerifiedDoubleReference < Base
44+
extend AutoCorrector
45+
include ConfigurableEnforcedStyle
46+
47+
MSG = 'Use a %<style>s class reference for verified doubles.'
48+
49+
RESTRICT_ON_SEND = Set[
50+
:class_double,
51+
:class_spy,
52+
:instance_double,
53+
:instance_spy,
54+
:mock_model,
55+
:object_double,
56+
:object_spy,
57+
:stub_model
58+
].freeze
59+
60+
REFERENCE_TYPE_STYLES = {
61+
str: :string,
62+
const: :constant
63+
}.freeze
64+
65+
# @!method verified_double(node)
66+
def_node_matcher :verified_double, <<~PATTERN
67+
(send
68+
nil?
69+
RESTRICT_ON_SEND
70+
$_class_reference
71+
...)
72+
PATTERN
73+
74+
def on_send(node)
75+
verified_double(node) do |class_reference|
76+
break correct_style_detected unless opposing_style?(class_reference)
77+
78+
message = format(MSG, style: style)
79+
expression = class_reference.loc.expression
80+
81+
add_offense(expression, message: message) do |corrector|
82+
violation = class_reference.children.last.to_s
83+
corrector.replace(expression, correct_style(violation))
84+
85+
opposite_style_detected
86+
end
87+
end
88+
end
89+
90+
private
91+
92+
def opposing_style?(class_reference)
93+
class_reference_style = REFERENCE_TYPE_STYLES[class_reference.type]
94+
95+
# Only enforce supported styles
96+
return false unless class_reference_style
97+
98+
class_reference_style != style
99+
end
100+
101+
def correct_style(violation)
102+
if style == :string
103+
"'#{violation}'"
104+
else
105+
violation
106+
end
107+
end
108+
end
109+
end
110+
end
111+
end

lib/rubocop/cop/rspec_cops.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
require_relative 'rspec/unspecified_exception'
9898
require_relative 'rspec/variable_definition'
9999
require_relative 'rspec/variable_name'
100+
require_relative 'rspec/verified_double_reference'
100101
require_relative 'rspec/verified_doubles'
101102
require_relative 'rspec/void_expect'
102103
require_relative 'rspec/yield'
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe RuboCop::Cop::RSpec::VerifiedDoubleReference do
4+
verified_doubles = %w[
5+
class_double
6+
class_spy
7+
instance_double
8+
instance_spy
9+
mock_model
10+
object_double
11+
object_spy
12+
stub_model
13+
]
14+
15+
verified_doubles.each do |verified_double|
16+
describe verified_double do
17+
context 'when EnforcedStyle is constant' do
18+
let(:cop_config) do
19+
{ 'EnforcedStyle' => 'constant' }
20+
end
21+
22+
it 'does not flag a violation when using a constant reference' do
23+
expect_no_offenses("#{verified_double}(ClassName)")
24+
end
25+
26+
it 'flags a violation when using a string reference' do
27+
expect_offense(<<~RUBY, verified_double: verified_double)
28+
%{verified_double}('ClassName')
29+
_{verified_double} ^^^^^^^^^^^ Use a constant class reference for verified doubles.
30+
RUBY
31+
32+
expect_correction(<<~RUBY)
33+
#{verified_double}(ClassName)
34+
RUBY
35+
end
36+
37+
include_examples 'detects style',
38+
"#{verified_double}(ClassName)",
39+
'constant'
40+
end
41+
42+
context 'when EnforcedStyle is string' do
43+
let(:cop_config) do
44+
{ 'EnforcedStyle' => 'string' }
45+
end
46+
47+
it 'does not flag a violation when using a string reference' do
48+
expect_no_offenses("#{verified_double}('ClassName')")
49+
end
50+
51+
it 'flags a violation when using a constant reference' do
52+
expect_offense(<<~RUBY, verified_double: verified_double)
53+
%{verified_double}(ClassName)
54+
_{verified_double} ^^^^^^^^^ Use a string class reference for verified doubles.
55+
RUBY
56+
57+
expect_correction(<<~RUBY)
58+
#{verified_double}('ClassName')
59+
RUBY
60+
end
61+
62+
include_examples 'detects style',
63+
"#{verified_double}('ClassName')",
64+
'string'
65+
end
66+
end
67+
end
68+
69+
it 'does not flag a violation when reference is not a supported style' do
70+
expect_no_offenses(<<~RUBY)
71+
klass = Array
72+
instance_double(klass)
73+
74+
@sut = Array
75+
let(:double) { instance_double(@sut) }
76+
77+
object_double([])
78+
79+
class_double(:Model)
80+
RUBY
81+
end
82+
end

0 commit comments

Comments
 (0)