diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e478777..f8e22cf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## master (unreleased) +### New features + +* [#117](https://github.com/rubocop/rubocop-minitest/issues/117): Add new cop `Minitest/AssertWithExpectedArgument` to check for unintended usages of `assert` instead of `assert_equal`. ([@cstyles][]) + ### Bug fixes * [#118](https://github.com/rubocop/rubocop-minitest/pull/118): **(BREAKING)** Fix `Minitest/AssertEmptyLiteral` by making it check for `assert_equal([], array)` instead of `assert([], array)`. ([@cstyles][]) diff --git a/config/default.yml b/config/default.yml index c9cfcf5e..9ce0b4e6 100644 --- a/config/default.yml +++ b/config/default.yml @@ -94,6 +94,11 @@ Minitest/AssertTruthy: Enabled: true VersionAdded: '0.2' +Minitest/AssertWithExpectedArgument: + Description: 'This cop tries to detect when a user accidentally used `assert` when they meant to use `assert_equal`.' + Enabled: pending + VersionAdded: '0.11' + Minitest/GlobalExpectations: Description: 'This cop checks for deprecated global expectations.' StyleGuide: 'https://minitest.rubystyle.guide#global-expectations' diff --git a/docs/modules/ROOT/pages/cops.adoc b/docs/modules/ROOT/pages/cops.adoc index 5447b4e8..cbf39ff5 100644 --- a/docs/modules/ROOT/pages/cops.adoc +++ b/docs/modules/ROOT/pages/cops.adoc @@ -27,6 +27,7 @@ based on the https://minitest.rubystyle.guide/[Minitest Style Guide]. * xref:cops_minitest.adoc#minitestassertrespondto[Minitest/AssertRespondTo] * xref:cops_minitest.adoc#minitestassertsilent[Minitest/AssertSilent] * xref:cops_minitest.adoc#minitestasserttruthy[Minitest/AssertTruthy] +* xref:cops_minitest.adoc#minitestassertwithexpectedargument[Minitest/AssertWithExpectedArgument] * xref:cops_minitest.adoc#minitestassertioninlifecyclehook[Minitest/AssertionInLifecycleHook] * xref:cops_minitest.adoc#minitestglobalexpectations[Minitest/GlobalExpectations] * xref:cops_minitest.adoc#minitestliteralasactualargument[Minitest/LiteralAsActualArgument] diff --git a/docs/modules/ROOT/pages/cops_minitest.adoc b/docs/modules/ROOT/pages/cops_minitest.adoc index 5d9982b4..e1f8f1d8 100644 --- a/docs/modules/ROOT/pages/cops_minitest.adoc +++ b/docs/modules/ROOT/pages/cops_minitest.adoc @@ -441,6 +441,35 @@ assert(actual, 'message') * https://minitest.rubystyle.guide#assert-truthy +== Minitest/AssertWithExpectedArgument + +|=== +| Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged + +| Pending +| Yes +| No +| 0.11 +| - +|=== + +This cop tries to detect when a user accidentally used +`assert` when they meant to use `assert_equal`. + +=== Examples + +[source,ruby] +---- +# bad +assert(3, my_list.length) +assert(expected, actual) + +# good +assert_equal(3, my_list.length) +assert_equal(expected, actual) +assert(foo, 'message') +---- + == Minitest/AssertionInLifecycleHook |=== diff --git a/lib/rubocop/cop/minitest/assert_with_expected_argument.rb b/lib/rubocop/cop/minitest/assert_with_expected_argument.rb new file mode 100644 index 00000000..938b447a --- /dev/null +++ b/lib/rubocop/cop/minitest/assert_with_expected_argument.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Minitest + # This cop tries to detect when a user accidentally used + # `assert` when they meant to use `assert_equal`. + # + # @example + # # bad + # assert(3, my_list.length) + # assert(expected, actual) + # + # # good + # assert_equal(3, my_list.length) + # assert_equal(expected, actual) + # assert(foo, 'message') + # + class AssertWithExpectedArgument < Cop + MSG = 'Did you mean to use `assert_equal(%s)`?' + RESTRICT_ON_SEND = %i[assert].freeze + + def_node_matcher :assert_with_two_arguments?, <<~PATTERN + (send nil? :assert $_ $_) + PATTERN + + def on_send(node) + assert_with_two_arguments?(node) do |_expected, message| + return if message.str_type? || message.dstr_type? + + arguments = node.arguments.map(&:source).join(', ') + add_offense(node, message: format(MSG, arguments: arguments)) + end + end + end + end + end +end diff --git a/lib/rubocop/cop/minitest_cops.rb b/lib/rubocop/cop/minitest_cops.rb index ec00c371..1022cd80 100644 --- a/lib/rubocop/cop/minitest_cops.rb +++ b/lib/rubocop/cop/minitest_cops.rb @@ -8,6 +8,7 @@ require_relative 'minitest/assert_empty_literal' require_relative 'minitest/assert_equal' require_relative 'minitest/assert_in_delta' +require_relative 'minitest/assert_with_expected_argument' require_relative 'minitest/assertion_in_lifecycle_hook' require_relative 'minitest/assert_kind_of' require_relative 'minitest/assert_nil' diff --git a/test/rubocop/cop/minitest/assert_with_expected_argument_test.rb b/test/rubocop/cop/minitest/assert_with_expected_argument_test.rb new file mode 100644 index 00000000..4995e429 --- /dev/null +++ b/test/rubocop/cop/minitest/assert_with_expected_argument_test.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +require 'test_helper' + +class AssertWithExpectedArgumentTest < Minitest::Test + def test_registers_offense_when_second_argument_is_not_a_string + assert_offense(<<~RUBY) + class FooTest < Minitest::Test + def test_do_something + my_list = [1, 2, 3] + assert(3, my_list.length) + ^^^^^^^^^^^^^^^^^^^^^^^^^ Did you mean to use `assert_equal(3, my_list.length)`? + end + end + RUBY + end + + def test_registers_offense_when_second_argument_is_a_variable + assert_offense(<<~RUBY) + class FooTest < Minitest::Test + def test_do_something + my_list_length = 3 + assert(3, my_list_length) + ^^^^^^^^^^^^^^^^^^^^^^^^^ Did you mean to use `assert_equal(3, my_list_length)`? + end + end + RUBY + end + + def test_does_not_register_offense_when_assert_is_called_with_one_argument + assert_no_offenses(<<~RUBY) + class FooTest < Minitest::Test + def test_do_something + assert(true) + end + end + RUBY + end + + def test_does_not_register_offense_when_second_argument_is_a_literal_string + assert_no_offenses(<<~RUBY) + class FooTest < Minitest::Test + def test_do_something + assert([], "empty array should be truthy") + end + end + RUBY + end + + def test_does_not_register_offense_when_second_argument_is_an_interpolated_string + assert_no_offenses(<<~'RUBY') + class FooTest < Minitest::Test + def test_do_something + additional_message = 'hello world' + assert([], "empty array should be truthy #{additional_message}") + end + end + RUBY + end + + def test_does_not_register_offense_when_using_assert_equal_with_two_arguments + assert_no_offenses(<<~'RUBY') + class FooTest < Minitest::Test + def test_do_something + assert_equal(3, actual) + end + end + RUBY + end +end