diff --git a/CHANGELOG.md b/CHANGELOG.md index 90c3e0e1..d76af990 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * [#92](https://github.com/rubocop-hq/rubocop-minitest/pull/92): Add new `Minitest/LiteralAsActualArgument` cop. ([@fatkodima][]) * [#91](https://github.com/rubocop-hq/rubocop-minitest/pull/91): Add new `Minitest/AssertInDelta` and `Minitest/RefuteInDelta` cops. ([@fatkodima][]) +* [#89](https://github.com/rubocop-hq/rubocop-minitest/pull/89): Add new `Minitest/TestMethodName` cop. ([@fatkodima][]) * [#83](https://github.com/rubocop-hq/rubocop-minitest/pull/83): New cops `AssertPathExists` and `RefutePathExists` check for use of `assert_path_exists`/`refute_path_exists` instead of `assert(File.exist?(path))`/`refute(File.exist?(path))`. ([@fatkodima][]) * [#88](https://github.com/rubocop-hq/rubocop-minitest/pull/88): Add new `Minitest/MultipleAssertions` cop. ([@fatkodima][]) * [#87](https://github.com/rubocop-hq/rubocop-minitest/pull/87): Add new `Minitest/AssertSilent` cop. ([@fatkodima][]) diff --git a/config/default.yml b/config/default.yml index 4e08fa94..e46a1d2d 100644 --- a/config/default.yml +++ b/config/default.yml @@ -167,6 +167,11 @@ Minitest/RefuteRespondTo: Enabled: true VersionAdded: '0.4' +Minitest/TestMethodName: + Description: 'This cop enforces that test method names start with `test_` prefix.' + Enabled: 'pending' + VersionAdded: '0.10' + Minitest/UnspecifiedException: Description: 'This cop checks for a specified error in `assert_raises`.' StyleGuide: 'https://minitest.rubystyle.guide#unspecified-exception' diff --git a/docs/modules/ROOT/pages/cops.adoc b/docs/modules/ROOT/pages/cops.adoc index 1073578d..4478f12a 100644 --- a/docs/modules/ROOT/pages/cops.adoc +++ b/docs/modules/ROOT/pages/cops.adoc @@ -29,6 +29,7 @@ * xref:cops_minitest.adoc#minitestrefutenil[Minitest/RefuteNil] * xref:cops_minitest.adoc#minitestrefutepathexists[Minitest/RefutePathExists] * xref:cops_minitest.adoc#minitestrefuterespondto[Minitest/RefuteRespondTo] +* xref:cops_minitest.adoc#minitesttestmethodname[Minitest/TestMethodName] * xref:cops_minitest.adoc#minitestunspecifiedexception[Minitest/UnspecifiedException] // END_COP_LIST diff --git a/docs/modules/ROOT/pages/cops_minitest.adoc b/docs/modules/ROOT/pages/cops_minitest.adoc index cfa17e46..92945629 100644 --- a/docs/modules/ROOT/pages/cops_minitest.adoc +++ b/docs/modules/ROOT/pages/cops_minitest.adoc @@ -884,6 +884,39 @@ refute_respond_to(self, :do_something) * https://minitest.rubystyle.guide#refute-respond-to +== Minitest/TestMethodName + +|=== +| Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged + +| Pending +| Yes +| Yes +| 0.10 +| - +|=== + +This cop enforces that test method names start with `test_` prefix. + +=== Examples + +[source,ruby] +---- +# bad +class FooTest < Minitest::Test + def does_something + assert_equal 42, do_something + end +end + +# good +class FooTest < Minitest::Test + def test_does_something + assert_equal 42, do_something + end +end +---- + == Minitest/UnspecifiedException |=== diff --git a/lib/rubocop/cop/minitest/test_method_name.rb b/lib/rubocop/cop/minitest/test_method_name.rb new file mode 100644 index 00000000..6ce17d18 --- /dev/null +++ b/lib/rubocop/cop/minitest/test_method_name.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Minitest + # This cop enforces that test method names start with `test_` prefix. + # + # @example + # # bad + # class FooTest < Minitest::Test + # def does_something + # assert_equal 42, do_something + # end + # end + # + # # good + # class FooTest < Minitest::Test + # def test_does_something + # assert_equal 42, do_something + # end + # end + # + class TestMethodName < Cop + include MinitestExplorationHelpers + include DefNode + + MSG = 'Test method name should start with `test_` prefix.' + + def on_class(class_node) + return unless test_class?(class_node) + + class_elements(class_node).each do |node| + add_offense(node, location: :name) if offense?(node) + end + end + + def autocorrect(node) + lambda do |corrector| + corrector.replace(node.loc.name, "test_#{node.method_name}") + end + end + + private + + def class_elements(class_node) + class_def = class_node.body + return [] unless class_def + + if class_def.def_type? + [class_def] + else + class_def.each_child_node(:def).to_a + end + end + + def offense?(node) + public?(node) && !test_method_name?(node) && !lifecycle_hook_method?(node) + end + + def public?(node) + !non_public?(node) + end + + def test_method_name?(node) + node.method_name.to_s.start_with?('test_') + end + end + end + end +end diff --git a/lib/rubocop/cop/minitest_cops.rb b/lib/rubocop/cop/minitest_cops.rb index 71770c7a..d4a07960 100644 --- a/lib/rubocop/cop/minitest_cops.rb +++ b/lib/rubocop/cop/minitest_cops.rb @@ -31,4 +31,5 @@ require_relative 'minitest/refute_instance_of' require_relative 'minitest/refute_path_exists' require_relative 'minitest/refute_respond_to' +require_relative 'minitest/test_method_name' require_relative 'minitest/unspecified_exception' diff --git a/lib/rubocop/cop/mixin/minitest_exploration_helpers.rb b/lib/rubocop/cop/mixin/minitest_exploration_helpers.rb index a213fc79..d99b5ea0 100644 --- a/lib/rubocop/cop/mixin/minitest_exploration_helpers.rb +++ b/lib/rubocop/cop/mixin/minitest_exploration_helpers.rb @@ -47,6 +47,15 @@ module MinitestExplorationHelpers refute_same ].to_set.freeze + LIFECYCLE_HOOK_METHODS = %i[ + before_setup + setup + after_setup + before_teardown + teardown + after_teardown + ].to_set.freeze + private def test_class?(class_node) @@ -70,6 +79,10 @@ def test_cases(class_node) def assertion?(node) node.send_type? && ASSERTIONS.include?(node.method_name) end + + def lifecycle_hook_method?(node) + node.def_type? && LIFECYCLE_HOOK_METHODS.include?(node.method_name) + end end end end diff --git a/test/rubocop/cop/minitest/refute_empty_test.rb b/test/rubocop/cop/minitest/refute_empty_test.rb index a6173b69..3ce4c7f5 100644 --- a/test/rubocop/cop/minitest/refute_empty_test.rb +++ b/test/rubocop/cop/minitest/refute_empty_test.rb @@ -85,7 +85,7 @@ def test_do_something RUBY end - def refute_empty_method + def test_refute_empty_method assert_no_offenses(<<~RUBY) class FooTest < Minitest::Test def test_do_something diff --git a/test/rubocop/cop/minitest/refute_instance_of_test.rb b/test/rubocop/cop/minitest/refute_instance_of_test.rb index 9fc033e7..35389b66 100644 --- a/test/rubocop/cop/minitest/refute_instance_of_test.rb +++ b/test/rubocop/cop/minitest/refute_instance_of_test.rb @@ -85,7 +85,7 @@ def test_do_something RUBY end - def refute_instance_of_method + def test_refute_instance_of_method assert_no_offenses(<<~RUBY) class FooTest < Minitest::Test def test_do_something diff --git a/test/rubocop/cop/minitest/test_method_name_test.rb b/test/rubocop/cop/minitest/test_method_name_test.rb new file mode 100644 index 00000000..a9d38aa1 --- /dev/null +++ b/test/rubocop/cop/minitest/test_method_name_test.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require 'test_helper' + +class TestMethodNameTest < Minitest::Test + def test_registers_offense_when_test_method_without_prefix + assert_offense(<<~RUBY) + class FooTest < Minitest::Test + def test_do_something + end + + def do_something_else + ^^^^^^^^^^^^^^^^^ Test method name should start with `test_` prefix. + end + end + RUBY + + assert_correction(<<~RUBY) + class FooTest < Minitest::Test + def test_do_something + end + + def test_do_something_else + end + end + RUBY + end + + def test_checks_only_test_classes + assert_no_offenses(<<~RUBY) + class FooTest + def do_something + end + end + RUBY + end + + def test_does_not_register_offense_when_hook_method + assert_no_offenses(<<~RUBY) + class FooTest < Minitest::Test + def setup + end + end + RUBY + end + + def test_does_not_register_offense_when_non_public_method + assert_no_offenses(<<~RUBY) + class FooTest < Minitest::Test + private + + def do_something + end + end + RUBY + end +end