Skip to content

Commit

Permalink
Merge pull request #260 from koic/add_new_minitest_assert_operator_an…
Browse files Browse the repository at this point in the history
…d_refute_operator_cops

[Fix #255] Add new `Minitest/AssertOperator` and `Minitest/RefuteOperator` cops
  • Loading branch information
koic authored Sep 22, 2023
2 parents 6832890 + 95e0cde commit 71f70d7
Show file tree
Hide file tree
Showing 8 changed files with 269 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* [#255](https://github.com/rubocop/rubocop-minitest/issues/255): Add new `Minitest/AssertOperator` and `Minitest/RefuteOperator` cops. ([@koic][])
12 changes: 12 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ Minitest/AssertNil:
Enabled: true
VersionAdded: '0.1'

Minitest/AssertOperator:
Description: 'This cop enforces the use of `assert_operator(expected, :<, actual)` over `assert(expected < actual)`.'
StyleGuide: 'https://minitest.rubystyle.guide#assert-operator'
Enabled: pending
VersionAdded: '<<next>>'

Minitest/AssertOutput:
Description: 'This cop checks for opportunities to use `assert_output`.'
StyleGuide: 'https://minitest.rubystyle.guide/#assert-output'
Expand Down Expand Up @@ -248,6 +254,12 @@ Minitest/RefuteNil:
Enabled: true
VersionAdded: '0.2'

Minitest/RefuteOperator:
Description: 'This cop enforces the use of `refute_operator(expected, :<, actual)` over `refute(expected < actual)`.'
StyleGuide: 'https://minitest.rubystyle.guide#refute-operator'
Enabled: pending
VersionAdded: '<<next>>'

Minitest/RefutePathExists:
Description: 'This cop enforces the test to use `refute_path_exists` instead of using `refute(File.exist?(path))`.'
StyleGuide: 'https://minitest.rubystyle.guide/#refute-path-exists'
Expand Down
53 changes: 53 additions & 0 deletions lib/rubocop/cop/minitest/assert_operator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Minitest
# Enforces the use of `assert_operator(expected, :<, actual)` over `assert(expected < actual)`.
#
# @example
#
# # bad
# assert(expected < actual)
#
# # good
# assert_operator(expected, :<, actual)
#
class AssertOperator < Base
extend AutoCorrector

MSG = 'Prefer using `assert_operator(%<new_arguments>s)`.'
RESTRICT_ON_SEND = %i[assert].freeze

def on_send(node)
return unless node.first_argument.operator_method?

new_arguments = build_new_arguments(node)

add_offense(node, message: format(MSG, new_arguments: new_arguments)) do |corrector|
corrector.replace(node.loc.selector, 'assert_operator')

corrector.replace(range_of_arguments(node), new_arguments)
end
end

private

def build_new_arguments(node)
lhs, op, rhs = *node.first_argument
new_arguments = "#{lhs.source}, :#{op}, #{rhs.source}"

if node.arguments.count == 2
new_arguments << ", #{node.last_argument.source}"
else
new_arguments
end
end

def range_of_arguments(node)
node.first_argument.source_range.begin.join(node.last_argument.source_range.end)
end
end
end
end
end
53 changes: 53 additions & 0 deletions lib/rubocop/cop/minitest/refute_operator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Minitest
# Enforces the use of `refute_operator(expected, :<, actual)` over `refute(expected < actual)`.
#
# @example
#
# # bad
# refute(expected < actual)
#
# # good
# refute_operator(expected, :<, actual)
#
class RefuteOperator < Base
extend AutoCorrector

MSG = 'Prefer using `refute_operator(%<new_arguments>s)`.'
RESTRICT_ON_SEND = %i[refute].freeze

def on_send(node)
return unless node.first_argument.operator_method?

new_arguments = build_new_arguments(node)

add_offense(node, message: format(MSG, new_arguments: new_arguments)) do |corrector|
corrector.replace(node.loc.selector, 'refute_operator')

corrector.replace(range_of_arguments(node), new_arguments)
end
end

private

def build_new_arguments(node)
lhs, op, rhs = *node.first_argument
new_arguments = "#{lhs.source}, :#{op}, #{rhs.source}"

if node.arguments.count == 2
new_arguments << ", #{node.last_argument.source}"
else
new_arguments
end
end

def range_of_arguments(node)
node.first_argument.source_range.begin.join(node.last_argument.source_range.end)
end
end
end
end
end
8 changes: 5 additions & 3 deletions lib/rubocop/cop/minitest_cops.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
require_relative 'minitest/assert_empty_literal'
require_relative 'minitest/assert_equal'
require_relative 'minitest/assert_in_delta'
require_relative 'minitest/assert_operator'
require_relative 'minitest/assert_predicate'
require_relative 'minitest/assert_raises_compound_body'
require_relative 'minitest/assert_raises_with_regexp_argument'
Expand Down Expand Up @@ -42,11 +43,12 @@
require_relative 'minitest/refute_false'
require_relative 'minitest/refute_equal'
require_relative 'minitest/refute_in_delta'
require_relative 'minitest/refute_kind_of'
require_relative 'minitest/refute_nil'
require_relative 'minitest/refute_includes'
require_relative 'minitest/refute_match'
require_relative 'minitest/refute_instance_of'
require_relative 'minitest/refute_kind_of'
require_relative 'minitest/refute_match'
require_relative 'minitest/refute_nil'
require_relative 'minitest/refute_operator'
require_relative 'minitest/refute_path_exists'
require_relative 'minitest/refute_predicate'
require_relative 'minitest/refute_respond_to'
Expand Down
2 changes: 1 addition & 1 deletion test/project_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ def test_default_rules_are_sorted_alphabetically
config_default = YAML.load_file('config/default.yml')

config_default.each_key do |key|
assert(previous_key <= key, "Cops should be sorted alphabetically. Please sort #{key}.")
assert_operator(previous_key, :<=, key, "Cops should be sorted alphabetically. Please sort #{key}.")
previous_key = key
end
end
Expand Down
72 changes: 72 additions & 0 deletions test/rubocop/cop/minitest/assert_operator_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# frozen_string_literal: true

require_relative '../../../test_helper'

class AssertOperatorTest < Minitest::Test
def test_registers_offense_when_using_assert_with_operator_method
assert_offense(<<~RUBY)
class FooTest < Minitest::Test
def test_do_something
assert(expected < actual)
^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `assert_operator(expected, :<, actual)`.
end
end
RUBY

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

def test_registers_offense_when_using_assert_with_operator_method_and_message
assert_offense(<<~RUBY)
class FooTest < Minitest::Test
def test_do_something
assert(expected < actual, 'message')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `assert_operator(expected, :<, actual, 'message')`.
end
end
RUBY

assert_correction(<<~RUBY)
class FooTest < Minitest::Test
def test_do_something
assert_operator(expected, :<, actual, 'message')
end
end
RUBY
end

def test_registers_offense_when_using_assert_with_operator_method_without_parentheses
assert_offense(<<~RUBY)
class FooTest < Minitest::Test
def test_do_something
assert expected < actual
^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `assert_operator(expected, :<, actual)`.
end
end
RUBY

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

def test_does_not_register_offense_when_using_not_assert_with_operator_method
assert_no_offenses(<<~RUBY)
class FooTest < Minitest::Test
def test_do_something
assert_operator(expected, :<, actual)
end
end
RUBY
end
end
72 changes: 72 additions & 0 deletions test/rubocop/cop/minitest/refute_operator_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# frozen_string_literal: true

require_relative '../../../test_helper'

class RefuteOperatorTest < Minitest::Test
def test_registers_offense_when_using_refute_with_operator_method
assert_offense(<<~RUBY)
class FooTest < Minitest::Test
def test_do_something
refute(expected < actual)
^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `refute_operator(expected, :<, actual)`.
end
end
RUBY

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

def test_registers_offense_when_using_refute_with_operator_method_and_message
assert_offense(<<~RUBY)
class FooTest < Minitest::Test
def test_do_something
refute(expected < actual, 'message')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `refute_operator(expected, :<, actual, 'message')`.
end
end
RUBY

assert_correction(<<~RUBY)
class FooTest < Minitest::Test
def test_do_something
refute_operator(expected, :<, actual, 'message')
end
end
RUBY
end

def test_registers_offense_when_using_refute_with_operator_method_without_parentheses
assert_offense(<<~RUBY)
class FooTest < Minitest::Test
def test_do_something
refute expected < actual
^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `refute_operator(expected, :<, actual)`.
end
end
RUBY

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

def test_does_not_register_offense_when_using_not_refute_with_operator_method
assert_no_offenses(<<~RUBY)
class FooTest < Minitest::Test
def test_do_something
refute_operator(expected, :<, actual)
end
end
RUBY
end
end

0 comments on commit 71f70d7

Please sign in to comment.