diff --git a/CHANGELOG.md b/CHANGELOG.md index ef954ca7..83bba441 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## master (unreleased) +* [#18](https://github.com/rubocop-hq/rubocop-minitest/pull/18): Add new `Minitest/RefuteFalse` cop. ([@duduribeiro][]) + ## 0.2.1 (2019-09-24) ### Bug fixes diff --git a/config/default.yml b/config/default.yml index 5f345835..831e989d 100644 --- a/config/default.yml +++ b/config/default.yml @@ -31,3 +31,9 @@ Minitest/RefuteNil: StyleGuide: 'https://github.com/rubocop-hq/minitest-style-guide#refute-nil' Enabled: true VersionAdded: '0.2' + +Minitest/RefuteFalse: + Description: 'Check if your test uses `refute(actual)` instead of `assert_equal(false, actual)`.' + StyleGuide: 'https://github.com/rubocop-hq/minitest-style-guide#refute-false' + Enabled: true + VersionAdded: '0.3' diff --git a/lib/rubocop/cop/minitest/refute_false.rb b/lib/rubocop/cop/minitest/refute_false.rb new file mode 100644 index 00000000..c44fb21d --- /dev/null +++ b/lib/rubocop/cop/minitest/refute_false.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Minitest + # Check if your test uses `refute(actual)` + # instead of `assert_equal(false, actual)`. + # + # @example + # # bad + # assert_equal(false, actual) + # assert_equal(false, actual, 'the message') + # + # # good + # refute(actual) + # refute(actual, 'the message') + # + class RefuteFalse < Cop + MSG = 'Prefer using `refute(%s)` over ' \ + '`assert_equal(false, %s)`.' + + def_node_matcher :assert_equal_with_false, <<~PATTERN + (send nil? :assert_equal false $_ $...) + PATTERN + + def on_send(node) + assert_equal_with_false(node) do |actual, rest_receiver_arg| + message = rest_receiver_arg.first + + arguments = [actual.source, message&.source].compact.join(', ') + + add_offense(node, message: format(MSG, arguments: arguments)) + end + end + + def autocorrect(node) + lambda do |corrector| + arguments = node.arguments.reject(&:false_type?) + replacement = arguments.map(&:source).join(', ') + corrector.replace(node.loc.expression, "refute(#{replacement})") + end + end + end + end + end +end diff --git a/lib/rubocop/cop/minitest_cops.rb b/lib/rubocop/cop/minitest_cops.rb index 75e82c2a..08452a4d 100644 --- a/lib/rubocop/cop/minitest_cops.rb +++ b/lib/rubocop/cop/minitest_cops.rb @@ -4,4 +4,5 @@ require_relative 'minitest/assert_nil' require_relative 'minitest/assert_includes' require_relative 'minitest/assert_truthy' +require_relative 'minitest/refute_false' require_relative 'minitest/refute_nil' diff --git a/manual/cops.md b/manual/cops.md index dc617dbc..e2538240 100644 --- a/manual/cops.md +++ b/manual/cops.md @@ -5,6 +5,7 @@ * [Minitest/AssertIncludes](cops_minitest.md#minitestassertincludes) * [Minitest/AssertNil](cops_minitest.md#minitestassertnil) * [Minitest/AssertTruthy](cops_minitest.md#minitestasserttruthy) +* [Minitest/RefuteFalse](cops_minitest.md#minitestrefutefalse) * [Minitest/RefuteNil](cops_minitest.md#minitestrefutenil) diff --git a/manual/cops_minitest.md b/manual/cops_minitest.md index 2d3097cd..bbf6fc17 100644 --- a/manual/cops_minitest.md +++ b/manual/cops_minitest.md @@ -98,6 +98,31 @@ assert(actual, 'the message') * [https://github.com/rubocop-hq/minitest-style-guide#assert-truthy](https://github.com/rubocop-hq/minitest-style-guide#assert-truthy) +## Minitest/RefuteFalse + +Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged +--- | --- | --- | --- | --- +Enabled | Yes | Yes | 0.3 | - + +Check if your test uses `refute(actual)` +instead of `assert_equal(false, actual)`. + +### Examples + +```ruby +# bad +assert_equal(false, actual) +assert_equal(false, actual, 'the message') + +# good +refute(actual) +refute(actual, 'the message') +``` + +### References + +* [https://github.com/rubocop-hq/minitest-style-guide#refute-false](https://github.com/rubocop-hq/minitest-style-guide#refute-false) + ## Minitest/RefuteNil Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged diff --git a/test/rubocop/cop/minitest/refute_false_test.rb b/test/rubocop/cop/minitest/refute_false_test.rb new file mode 100644 index 00000000..1b2bac69 --- /dev/null +++ b/test/rubocop/cop/minitest/refute_false_test.rb @@ -0,0 +1,120 @@ +# frozen_string_literal: true + +require 'test_helper' + +class RefuteFalseTest < Minitest::Test + def setup + @cop = RuboCop::Cop::Minitest::RefuteFalse.new + end + + def test_registers_offense_when_using_assert_equal_with_false + assert_offense(<<~RUBY, @cop) + class FooTest < Minitest::Test + def test_do_something + assert_equal(false, somestuff) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `refute(somestuff)` over `assert_equal(false, somestuff)`. + end + end + RUBY + + assert_correction(<<~RUBY, @cop) + class FooTest < Minitest::Test + def test_do_something + refute(somestuff) + end + end + RUBY + end + + def test_registers_offense_when_using_assert_equal_with_false_and_message + assert_offense(<<~RUBY, @cop) + class FooTest < Minitest::Test + def test_do_something + assert_equal(false, somestuff, 'the message') + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `refute(somestuff, 'the message')` over `assert_equal(false, somestuff, 'the message')`. + end + end + RUBY + + assert_correction(<<~RUBY, @cop) + class FooTest < Minitest::Test + def test_do_something + refute(somestuff, 'the message') + end + end + RUBY + end + + def test_registers_offense_when_using_assert_equal_with_method_call + assert_offense(<<~RUBY, @cop) + class FooTest < Minitest::Test + def test_do_something + assert_equal(false, obj.do_something, 'the message') + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `refute(obj.do_something, 'the message')` over `assert_equal(false, obj.do_something, 'the message')`. + end + end + RUBY + + assert_correction(<<~RUBY, @cop) + class FooTest < Minitest::Test + def test_do_something + refute(obj.do_something, 'the message') + end + end + RUBY + end + + def test_registers_offense_when_using_assert_equal_with_variable_message + assert_offense(<<~RUBY, @cop) + class FooTest < Minitest::Test + def test_do_something + message = 'the message' + assert_equal(false, somestuff, message) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `refute(somestuff, message)` over `assert_equal(false, somestuff, message)`. + end + end + RUBY + + assert_correction(<<~RUBY, @cop) + class FooTest < Minitest::Test + def test_do_something + message = 'the message' + refute(somestuff, message) + end + end + RUBY + end + + def test_registers_offense_when_using_assert_equal_with_constant_message + assert_offense(<<~RUBY, @cop) + class FooTest < Minitest::Test + MESSAGE = 'the message' + + def test_do_something + assert_equal(false, somestuff, MESSAGE) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `refute(somestuff, MESSAGE)` over `assert_equal(false, somestuff, MESSAGE)`. + end + end + RUBY + + assert_correction(<<~RUBY, @cop) + class FooTest < Minitest::Test + MESSAGE = 'the message' + + def test_do_something + refute(somestuff, MESSAGE) + end + end + RUBY + end + + def test_does_not_register_offense_when_using_refute_method + assert_no_offenses(<<~RUBY, @cop) + class FooTest < Minitest::Test + def test_do_something + refute(somestuff) + end + end + RUBY + end +end