Skip to content

Commit 6b1ea4c

Browse files
committed
Add support a_block_changing and changing for RSpec/ChangeByZero
This PR is add support `a_block_changing` and `changing` for `RSpec/ChangeByZero`.
1 parent 7226265 commit 6b1ea4c

File tree

3 files changed

+64
-21
lines changed

3 files changed

+64
-21
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- Fix a false positive in `RSpec/IndexedLet` with suffixes after index-like numbers. ([@pirj])
66
- Fix an error for `RSpec/Rails/HaveHttpStatus` with comparison with strings containing non-numeric characters. ([@ydah])
77
- Fix an error for `RSpec/MatchArray` when `match_array` with no argument. ([@ydah])
8+
- Add support `a_block_changing` and `changing` for `RSpec/ChangeByZero`. ([@ydah])
89

910
## 2.20.0 (2023-04-18)
1011

lib/rubocop/cop/rspec/change_by_zero.rb

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -59,64 +59,78 @@ module RSpec
5959
#
6060
class ChangeByZero < Base
6161
extend AutoCorrector
62-
MSG = 'Prefer `not_to change` over `to change.by(0)`.'
62+
MSG = 'Prefer `not_to change` over `to %<method>s.by(0)`.'
6363
MSG_COMPOUND = 'Prefer %<preferred>s with compound expectations ' \
64-
'over `change.by(0)`.'
65-
RESTRICT_ON_SEND = %i[change].freeze
64+
'over `%<method>s.by(0)`.'
65+
CHANGE_METHODS = Set[:change, :a_block_changing, :changing].freeze
66+
RESTRICT_ON_SEND = CHANGE_METHODS.freeze
6667

6768
# @!method expect_change_with_arguments(node)
6869
def_node_matcher :expect_change_with_arguments, <<-PATTERN
6970
(send
70-
(send nil? :change ...) :by
71+
$(send nil? CHANGE_METHODS ...) :by
7172
(int 0))
7273
PATTERN
7374

7475
# @!method expect_change_with_block(node)
7576
def_node_matcher :expect_change_with_block, <<-PATTERN
7677
(send
7778
(block
78-
(send nil? :change)
79+
$(send nil? CHANGE_METHODS)
7980
(args)
8081
(send (...) _)) :by
8182
(int 0))
8283
PATTERN
8384

8485
# @!method change_nodes(node)
8586
def_node_search :change_nodes, <<-PATTERN
86-
$(send nil? :change ...)
87+
$(send nil? CHANGE_METHODS ...)
8788
PATTERN
8889

8990
def on_send(node)
90-
expect_change_with_arguments(node.parent) do
91-
check_offense(node.parent)
91+
expect_change_with_arguments(node.parent) do |change|
92+
register_offense(node.parent, change)
9293
end
9394

94-
expect_change_with_block(node.parent.parent) do
95-
check_offense(node.parent.parent)
95+
expect_change_with_block(node.parent.parent) do |change|
96+
register_offense(node.parent.parent, change)
9697
end
9798
end
9899

99100
private
100101

101-
def check_offense(node)
102-
expression = node.source_range
102+
# rubocop:disable Metrics/MethodLength
103+
def register_offense(node, change_node)
103104
if compound_expectations?(node)
104-
add_offense(expression, message: message_compound) do |corrector|
105+
add_offense(node.source_range,
106+
message: message_compound(change_node)) do |corrector|
105107
autocorrect_compound(corrector, node)
106108
end
107109
else
108-
add_offense(expression) do |corrector|
109-
autocorrect(corrector, node)
110+
add_offense(node.source_range,
111+
message: message(change_node)) do |corrector|
112+
autocorrect(corrector, node, change_node)
110113
end
111114
end
112115
end
116+
# rubocop:enable Metrics/MethodLength
113117

114118
def compound_expectations?(node)
115119
%i[and or & |].include?(node.parent.method_name)
116120
end
117121

118-
def autocorrect(corrector, node)
122+
def message(change_node)
123+
format(MSG, method: change_node.method_name)
124+
end
125+
126+
def message_compound(change_node)
127+
format(MSG_COMPOUND, preferred: preferred_method,
128+
method: change_node.method_name)
129+
end
130+
131+
def autocorrect(corrector, node, change_node)
119132
corrector.replace(node.parent.loc.selector, 'not_to')
133+
corrector.replace(change_node.loc.selector, 'change')
120134
range = node.loc.dot.with(end_pos: node.source_range.end_pos)
121135
corrector.remove(range)
122136
end
@@ -135,10 +149,6 @@ def negated_matcher
135149
cop_config['NegatedMatcher']
136150
end
137151

138-
def message_compound
139-
format(MSG_COMPOUND, preferred: preferred_method)
140-
end
141-
142152
def preferred_method
143153
negated_matcher ? "`#{negated_matcher}`" : 'negated matchers'
144154
end

spec/rubocop/cop/rspec/change_by_zero_spec.rb

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# frozen_string_literal: true
22

33
RSpec.describe RuboCop::Cop::RSpec::ChangeByZero, :config do
4-
it 'registers an offense when the argument to `by` is zero' do
4+
it 'registers an offense when using `change` and argument to `by` is zero' do
55
expect_offense(<<-RUBY)
66
it do
77
expect { foo }.to change(Foo, :bar).by(0)
@@ -25,6 +25,38 @@
2525
RUBY
2626
end
2727

28+
it 'registers an offense when using `a_block_changing` ' \
29+
'and argument to `by` is zero' do
30+
expect_offense(<<-RUBY)
31+
it do
32+
expect { foo }.to a_block_changing(Foo, :bar).by(0)
33+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer `not_to change` over `to a_block_changing.by(0)`.
34+
end
35+
RUBY
36+
37+
expect_correction(<<-RUBY)
38+
it do
39+
expect { foo }.not_to change(Foo, :bar)
40+
end
41+
RUBY
42+
end
43+
44+
it 'registers an offense when using `changing` ' \
45+
'and argument to `by` is zero' do
46+
expect_offense(<<-RUBY)
47+
it do
48+
expect { foo }.to changing(Foo, :bar).by(0)
49+
^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer `not_to change` over `to changing.by(0)`.
50+
end
51+
RUBY
52+
53+
expect_correction(<<-RUBY)
54+
it do
55+
expect { foo }.not_to change(Foo, :bar)
56+
end
57+
RUBY
58+
end
59+
2860
context 'when `NegatedMatcher` is not defined (default)' do
2961
it 'registers an offense when the argument to `by` is zero ' \
3062
'with compound expectations by `and`' do

0 commit comments

Comments
 (0)