Skip to content

Commit

Permalink
Include a cop to enforce refute_equal over assert of a negation (#21)
Browse files Browse the repository at this point in the history
This commit adds a Cop that checks some usage of
`assert(some negations)` and enforces the usage of
`refute_eqaul(expected, actual)` instead.
  • Loading branch information
duduribeiro authored and bbatsov committed Oct 11, 2019
1 parent 68bc547 commit 38729e2
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* [#15](https://github.com/rubocop-hq/rubocop-minitest/pull/15): Add new `Minitest/RefuteIncludes` cop. ([@abhaynikam][])
* [#18](https://github.com/rubocop-hq/rubocop-minitest/pull/18): Add new `Minitest/RefuteFalse` cop. ([@duduribeiro][])
* [#20](https://github.com/rubocop-hq/rubocop-minitest/pull/20): Add new `Minitest/RefuteEmpty` cop. ([@abhaynikam][])
* [#21](https://github.com/rubocop-hq/rubocop-minitest/pull/21): Add new `Minitest/RefuteEqual` cop. ([@duduribeiro][])

### Bug fixes

Expand Down
6 changes: 6 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ Minitest/AssertTruthy:
Enabled: true
VersionAdded: '0.2'

Minitest/RefuteEqual:
Description: 'Check if your test uses `refute_equal` instead of `assert(expected != object)` or `assert(! expected == object))`.'
StyleGuide: 'https://github.com/rubocop-hq/minitest-style-guide#refute-equal'
Enabled: true
VersionAdded: '0.3'

Minitest/RefuteNil:
Description: 'This cop enforces the test to use `refute_nil` instead of using `refute_equal(nil, something)`.'
StyleGuide: 'https://github.com/rubocop-hq/minitest-style-guide#refute-nil'
Expand Down
69 changes: 69 additions & 0 deletions lib/rubocop/cop/minitest/refute_equal.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Minitest
# This cop enforces the usages of `refute_equal(expected, object)`
# over `assert_equal(expected != actual)` or `assert(! expected -= actual)`.
#
# @example
# # bad
# assert("rubocop-minitest" != actual)
# assert(! "rubocop-minitest" == actual)
#
# # good
# refute_equal("rubocop-minitest", actual)
#
class RefuteEqual < Cop
MSG = 'Prefer using `refute_equal(%<preferred>s)` over ' \
'`assert(%<over>s)`.'

def_node_matcher :assert_not_equal, <<~PATTERN
(send nil? :assert ${(send $_ :!= $_) (send (send $_ :! ) :== $_) } $... )
PATTERN

def on_send(node)
preferred, over = process_not_equal(node)
return unless preferred && over

message = format(MSG, preferred: preferred, over: over)
add_offense(node, message: message)
end

def autocorrect(node)
lambda do |corrector|
assert_not_equal(node) do |_, first_arg, second_arg, rest_args|
autocorrect_node(node, corrector, first_arg, second_arg, rest_args)
end
end
end

private

def autocorrect_node(node, corrector, first_arg, second_arg, rest_args)
custom_message = rest_args.first
replacement = preferred_usage(first_arg, second_arg, custom_message)
corrector.replace(node.loc.expression, "refute_equal(#{replacement})")
end

def preferred_usage(first_arg, second_arg, custom_message = nil)
[first_arg, second_arg, custom_message]
.compact.map(&:source).join(', ')
end

def original_usage(first_part, custom_message)
[first_part, custom_message].compact.join(', ')
end

def process_not_equal(node)
assert_not_equal(node) do |over, first_arg, second_arg, rest_args|
custom_message = rest_args.first
preferred = preferred_usage(first_arg, second_arg, custom_message)
over = original_usage(over.source, custom_message&.source)
return [preferred, over]
end
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/rubocop/cop/minitest_cops.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
require_relative 'minitest/assert_truthy'
require_relative 'minitest/refute_empty'
require_relative 'minitest/refute_false'
require_relative 'minitest/refute_equal'
require_relative 'minitest/refute_nil'
require_relative 'minitest/refute_includes'
1 change: 1 addition & 0 deletions manual/cops.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* [Minitest/AssertNil](cops_minitest.md#minitestassertnil)
* [Minitest/AssertTruthy](cops_minitest.md#minitestasserttruthy)
* [Minitest/RefuteEmpty](cops_minitest.md#minitestrefuteempty)
* [Minitest/RefuteEqual](cops_minitest.md#minitestrefuteequal)
* [Minitest/RefuteFalse](cops_minitest.md#minitestrefutefalse)
* [Minitest/RefuteIncludes](cops_minitest.md#minitestrefuteincludes)
* [Minitest/RefuteNil](cops_minitest.md#minitestrefutenil)
Expand Down
24 changes: 24 additions & 0 deletions manual/cops_minitest.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,30 @@ refute_empty(object, 'the message')

* [https://github.com/rubocop-hq/minitest-style-guide#refute-empty](https://github.com/rubocop-hq/minitest-style-guide#refute-empty)

## Minitest/RefuteEqual

Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
--- | --- | --- | --- | ---
Enabled | Yes | Yes | 0.3 | -

This cop enforces the usages of `refute_equal(expected, object)`
over `assert_equal(expected != actual)` or `assert(! expected -= actual)`.

### Examples

```ruby
# bad
assert("rubocop-minitest" != actual)
assert(! "rubocop-minitest" == actual)

# good
refute_equal("rubocop-minitest", actual)
```

### References

* [https://github.com/rubocop-hq/minitest-style-guide#refute-equal](https://github.com/rubocop-hq/minitest-style-guide#refute-equal)

## Minitest/RefuteFalse

Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
Expand Down
133 changes: 133 additions & 0 deletions test/rubocop/cop/minitest/refute_equal_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# frozen_string_literal: true

require 'test_helper'

class RefuteEqualTest < Minitest::Test
def setup
@cop = RuboCop::Cop::Minitest::RefuteEqual.new
end

def test_registers_offense_when_using_assert_not_equal_operator_with_string
assert_offense(<<~RUBY, @cop)
class FooTest < Minitest::Test
def test_do_something
assert('rubocop-minitest' != actual)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `refute_equal('rubocop-minitest', actual)` over `assert('rubocop-minitest' != actual)`.
end
end
RUBY

assert_correction(<<~RUBY, @cop)
class FooTest < Minitest::Test
def test_do_something
refute_equal('rubocop-minitest', actual)
end
end
RUBY
end

def test_registers_offense_when_using_assert_not_equal_operator_with_object
assert_offense(<<~RUBY, @cop)
class FooTest < Minitest::Test
def test_do_something
assert(expected != actual)
^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `refute_equal(expected, actual)` over `assert(expected != actual)`.
end
end
RUBY

assert_correction(<<~RUBY, @cop)
class FooTest < Minitest::Test
def test_do_something
refute_equal(expected, actual)
end
end
RUBY
end

def test_registers_offense_when_using_assert_not_equal_operator_with_method_call
assert_offense(<<~RUBY, @cop)
class FooTest < Minitest::Test
def test_do_something
assert(obj.expected != other_obj.actual)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `refute_equal(obj.expected, other_obj.actual)` over `assert(obj.expected != other_obj.actual)`.
end
end
RUBY

assert_correction(<<~RUBY, @cop)
class FooTest < Minitest::Test
def test_do_something
refute_equal(obj.expected, other_obj.actual)
end
end
RUBY
end

def test_registers_offense_when_using_assert_not_equal_operator_with_the_message
assert_offense(<<~RUBY, @cop)
class FooTest < Minitest::Test
def test_do_something
assert('rubocop-minitest' != actual, 'the message')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `refute_equal('rubocop-minitest', actual, 'the message')` over `assert('rubocop-minitest' != actual, 'the message')`.
end
end
RUBY

assert_correction(<<~RUBY, @cop)
class FooTest < Minitest::Test
def test_do_something
refute_equal('rubocop-minitest', actual, 'the message')
end
end
RUBY
end

def test_registers_offense_when_using_negate_equals
assert_offense(<<~RUBY, @cop)
class FooTest < Minitest::Test
def test_do_something
assert(! 'rubocop-minitest' == object)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `refute_equal('rubocop-minitest', object)` over `assert(! 'rubocop-minitest' == object)`.
end
end
RUBY

assert_correction(<<~RUBY, @cop)
class FooTest < Minitest::Test
def test_do_something
refute_equal('rubocop-minitest', object)
end
end
RUBY
end

def test_registers_offense_when_using_negate_equals_with_message
assert_offense(<<~RUBY, @cop)
class FooTest < Minitest::Test
def test_do_something
assert(! 'rubocop-minitest' == object, 'the message')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `refute_equal('rubocop-minitest', object, 'the message')` over `assert(! 'rubocop-minitest' == object, 'the message')`.
end
end
RUBY

assert_correction(<<~RUBY, @cop)
class FooTest < Minitest::Test
def test_do_something
refute_equal('rubocop-minitest', object, 'the message')
end
end
RUBY
end

def test_does_not_register_offense_when_using_refute_equal
assert_no_offenses(<<~RUBY, @cop)
class FooTest < Minitest::Test
def test_do_something
refute_equal('rubocop-minitest', actual)
end
end
RUBY
end
end

0 comments on commit 38729e2

Please sign in to comment.