class RuboCop::Cop::RSpec::ChangeByZero
.and not_change { Foo.baz }
.to not_change { Foo.bar }
expect { run }
.and not_change(Foo, :baz)
.to not_change(Foo, :bar)
expect { run }
define_negated_matcher :not_change, :change
# good
.and change { Foo.baz }.by(0)
.to change { Foo.bar }.by(0)
expect { run }
.and change(Foo, :baz).by(0)
.to change(Foo, :bar).by(0)
expect { run }
# bad (support autocorrection to good case)
@example NegatedMatcher: not_change
.and not_change { Foo.baz }
.to not_change { Foo.bar }
expect { run }
.and not_change(Foo, :baz)
.to not_change(Foo, :bar)
expect { run }
define_negated_matcher :not_change, :change
# good - compound expectations
expect { run }.not_to change { Foo.bar }
expect { run }.not_to change(Foo, :bar)
# good
.and change { Foo.baz }.by(0)
.to change { Foo.bar }.by(0)
expect { run }
.and change(Foo, :baz).by(0)
.to change(Foo, :bar).by(0)
expect { run }
# bad - compound expectations (does not support autocorrection)
expect { run }.to change { Foo.bar }.by(0)
expect { run }.to change(Foo, :bar).by(0)
# bad
@example NegatedMatcher: ~ (default)
the ‘NegatedMatcher` option, the cop will perform the autocorrection.
negated matcher for `change`, e.g. `not_change` with
compound expectations, but if you set the
By default the cop does not support autocorrect of
negation matchers of `RSpec::Matchers#change`.
In the case of composite expectations, cop suggest using the
Prefer negated matchers over `to change.by(0)`.
def autocorrect(corrector, node)
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
def autocorrect_compound(corrector, node)
def autocorrect_compound(corrector, node) return unless negated_matcher change_nodes(node) do |change_node| corrector.replace(change_node.loc.selector, negated_matcher) range = node.loc.dot.with(end_pos: node.loc.expression.end_pos) corrector.remove(range) end end
def check_offense(node)
def check_offense(node) expression = node.loc.expression if compound_expectations?(node) add_offense(expression, message: message_compound) do |corrector| autocorrect_compound(corrector, node) end else add_offense(expression) do |corrector| autocorrect(corrector, node) end end end
def compound_expectations?(node)
def compound_expectations?(node) %i[and or & |].include?(node.parent.method_name) end
def message_compound
def message_compound format(MSG_COMPOUND, preferred: preferred_method) end
def negated_matcher
def negated_matcher cop_config['NegatedMatcher'] end
def on_send(node)
def on_send(node) expect_change_with_arguments(node.parent) do check_offense(node.parent) end expect_change_with_block(node.parent.parent) do check_offense(node.parent.parent) end end
def preferred_method
def preferred_method negated_matcher ? "`#{negated_matcher}`" : 'negated matchers' end