-
-
Notifications
You must be signed in to change notification settings - Fork 277
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Resolves: #1100 This cop enforces the use of `not_to change` over `to change.by(0)` ## example ```ruby # bad expect { run }.to change(Foo, :bar).by(0) expect { run }.to change { Foo.bar }.by(0) # good expect { run }.not_to change(Foo, :bar) expect { run }.not_to change { Foo.bar } ```
- Loading branch information
Showing
7 changed files
with
204 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
# frozen_string_literal: true | ||
|
||
module RuboCop | ||
module Cop | ||
module RSpec | ||
# Prefer negated matchers over `to change.by(0)`. | ||
# | ||
# @example | ||
# # bad | ||
# expect { run }.to change(Foo, :bar).by(0) | ||
# expect { run }.to change { Foo.bar }.by(0) | ||
# expect { run } | ||
# .to change(Foo, :bar).by(0) | ||
# .and change(Foo, :baz).by(0) | ||
# expect { run } | ||
# .to change { Foo.bar }.by(0) | ||
# .and change { Foo.baz }.by(0) | ||
# | ||
# # good | ||
# expect { run }.not_to change(Foo, :bar) | ||
# expect { run }.not_to change { Foo.bar } | ||
# expect { run } | ||
# .to not_change(Foo, :bar) | ||
# .and not_change(Foo, :baz) | ||
# expect { run } | ||
# .to not_change { Foo.bar } | ||
# .and not_change { Foo.baz } | ||
# | ||
class ChangeByZero < Base | ||
extend AutoCorrector | ||
MSG = 'Prefer `not_to change` over `to change.by(0)`.' | ||
MSG_COMPOUND = 'Prefer negated matchers with compound expectations ' \ | ||
'over `change.by(0)`.' | ||
|
||
# @!method expect_change_with_arguments(node) | ||
def_node_matcher :expect_change_with_arguments, <<-PATTERN | ||
(send | ||
(send nil? :change ...) :by | ||
(int 0)) | ||
PATTERN | ||
|
||
# @!method expect_change_with_block(node) | ||
def_node_matcher :expect_change_with_block, <<-PATTERN | ||
(send | ||
(block | ||
(send nil? :change) | ||
(args) | ||
(send (...) $_)) :by | ||
(int 0)) | ||
PATTERN | ||
|
||
def on_send(node) | ||
expect_change_with_arguments(node) do | ||
check_offence(node) | ||
end | ||
|
||
expect_change_with_block(node) do | ||
check_offence(node) | ||
end | ||
end | ||
|
||
private | ||
|
||
def check_offence(node) | ||
expression = node.loc.expression | ||
if compound_expectations?(node) | ||
add_offense(expression, message: MSG_COMPOUND) | ||
else | ||
add_offense(expression) do |corrector| | ||
autocorrect(corrector, node) | ||
end | ||
end | ||
end | ||
|
||
def compound_expectations?(node) | ||
%i[and or].include?(node.parent.method_name) | ||
end | ||
|
||
def autocorrect(corrector, node) | ||
corrector.replace(node.parent.loc.selector, 'not_to') | ||
range = node.loc.dot.with(end_pos: node.loc.expression.end_pos) | ||
corrector.remove(range) | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
# frozen_string_literal: true | ||
|
||
RSpec.describe RuboCop::Cop::RSpec::ChangeByZero do | ||
it 'registers an offense when the argument to `by` is zero' do | ||
expect_offense(<<-RUBY) | ||
it do | ||
expect { foo }.to change(Foo, :bar).by(0) | ||
^^^^^^^^^^^^^^^^^^^^^^^ Prefer `not_to change` over `to change.by(0)`. | ||
expect { foo }.to change(::Foo, :bar).by(0) | ||
^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer `not_to change` over `to change.by(0)`. | ||
expect { foo }.to change { Foo.bar }.by(0) | ||
^^^^^^^^^^^^^^^^^^^^^^^^ Prefer `not_to change` over `to change.by(0)`. | ||
expect { foo }.to change(Foo, :bar).by 0 | ||
^^^^^^^^^^^^^^^^^^^^^^ Prefer `not_to change` over `to change.by(0)`. | ||
end | ||
RUBY | ||
|
||
expect_correction(<<-RUBY) | ||
it do | ||
expect { foo }.not_to change(Foo, :bar) | ||
expect { foo }.not_to change(::Foo, :bar) | ||
expect { foo }.not_to change { Foo.bar } | ||
expect { foo }.not_to change(Foo, :bar) | ||
end | ||
RUBY | ||
end | ||
|
||
it 'registers an offense when the argument to `by` is zero ' \ | ||
'with compound expectations' do | ||
expect_offense(<<-RUBY) | ||
it do | ||
expect { foo } | ||
.to change(Foo, :bar).by(0) | ||
^^^^^^^^^^^^^^^^^^^^^^^ Prefer negated matchers with compound expectations over `change.by(0)`. | ||
.and change(Foo, :baz).by(0) | ||
^^^^^^^^^^^^^^^^^^^^^^^ Prefer negated matchers with compound expectations over `change.by(0)`. | ||
expect { foo } | ||
.to change { Foo.bar }.by(0) | ||
^^^^^^^^^^^^^^^^^^^^^^^^ Prefer negated matchers with compound expectations over `change.by(0)`. | ||
.and change { Foo.baz }.by(0) | ||
^^^^^^^^^^^^^^^^^^^^^^^^ Prefer negated matchers with compound expectations over `change.by(0)`. | ||
expect { foo } | ||
.to change(Foo, :bar).by(0) | ||
^^^^^^^^^^^^^^^^^^^^^^^ Prefer negated matchers with compound expectations over `change.by(0)`. | ||
.or change(Foo, :baz).by(0) | ||
^^^^^^^^^^^^^^^^^^^^^^^ Prefer negated matchers with compound expectations over `change.by(0)`. | ||
expect { foo } | ||
.to change { Foo.bar }.by(0) | ||
^^^^^^^^^^^^^^^^^^^^^^^^ Prefer negated matchers with compound expectations over `change.by(0)`. | ||
.or change { Foo.baz }.by(0) | ||
^^^^^^^^^^^^^^^^^^^^^^^^ Prefer negated matchers with compound expectations over `change.by(0)`. | ||
end | ||
RUBY | ||
end | ||
|
||
it 'does not register an offense when the argument to `by` is not zero' do | ||
expect_no_offenses(<<-RUBY) | ||
it do | ||
expect { foo }.to change(Foo, :bar).by(1) | ||
expect { foo }.to change { Foo.bar }.by(1) | ||
end | ||
RUBY | ||
end | ||
end |