rubocop-minitest 0.6.2 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +0 -3
  3. data/.gitattributes +1 -0
  4. data/.rubocop.yml +2 -1
  5. data/.rubocop_todo.yml +0 -7
  6. data/CHANGELOG.md +65 -0
  7. data/Gemfile +1 -1
  8. data/README.md +5 -1
  9. data/Rakefile +29 -0
  10. data/config/default.yml +103 -18
  11. data/docs/antora.yml +7 -0
  12. data/docs/modules/ROOT/nav.adoc +6 -0
  13. data/docs/modules/ROOT/pages/cops.adoc +37 -0
  14. data/docs/modules/ROOT/pages/cops_minitest.adoc +1014 -0
  15. data/docs/modules/ROOT/pages/index.adoc +5 -0
  16. data/docs/modules/ROOT/pages/installation.adoc +15 -0
  17. data/docs/modules/ROOT/pages/usage.adoc +32 -0
  18. data/{manual → legacy-docs}/cops.md +1 -0
  19. data/{manual → legacy-docs}/cops_minitest.md +64 -41
  20. data/{manual → legacy-docs}/index.md +0 -0
  21. data/{manual → legacy-docs}/installation.md +0 -0
  22. data/{manual → legacy-docs}/usage.md +0 -0
  23. data/lib/rubocop/cop/generator.rb +56 -0
  24. data/lib/rubocop/cop/minitest/assert_empty.rb +4 -30
  25. data/lib/rubocop/cop/minitest/assert_empty_literal.rb +15 -0
  26. data/lib/rubocop/cop/minitest/assert_equal.rb +2 -31
  27. data/lib/rubocop/cop/minitest/assert_in_delta.rb +27 -0
  28. data/lib/rubocop/cop/minitest/assert_includes.rb +4 -4
  29. data/lib/rubocop/cop/minitest/assert_instance_of.rb +4 -38
  30. data/lib/rubocop/cop/minitest/assert_kind_of.rb +25 -0
  31. data/lib/rubocop/cop/minitest/assert_match.rb +4 -39
  32. data/lib/rubocop/cop/minitest/assert_nil.rb +2 -2
  33. data/lib/rubocop/cop/minitest/assert_output.rb +49 -0
  34. data/lib/rubocop/cop/minitest/assert_path_exists.rb +58 -0
  35. data/lib/rubocop/cop/minitest/assert_respond_to.rb +10 -45
  36. data/lib/rubocop/cop/minitest/assert_silent.rb +45 -0
  37. data/lib/rubocop/cop/minitest/assert_truthy.rb +2 -2
  38. data/lib/rubocop/cop/minitest/assertion_in_lifecycle_hook.rb +43 -0
  39. data/lib/rubocop/cop/minitest/global_expectations.rb +95 -0
  40. data/lib/rubocop/cop/minitest/literal_as_actual_argument.rb +52 -0
  41. data/lib/rubocop/cop/minitest/multiple_assertions.rb +63 -0
  42. data/lib/rubocop/cop/minitest/refute_empty.rb +4 -30
  43. data/lib/rubocop/cop/minitest/refute_false.rb +3 -3
  44. data/lib/rubocop/cop/minitest/refute_in_delta.rb +27 -0
  45. data/lib/rubocop/cop/minitest/refute_includes.rb +4 -4
  46. data/lib/rubocop/cop/minitest/refute_instance_of.rb +4 -38
  47. data/lib/rubocop/cop/minitest/refute_kind_of.rb +25 -0
  48. data/lib/rubocop/cop/minitest/refute_match.rb +4 -39
  49. data/lib/rubocop/cop/minitest/refute_nil.rb +2 -2
  50. data/lib/rubocop/cop/minitest/refute_path_exists.rb +58 -0
  51. data/lib/rubocop/cop/minitest/refute_respond_to.rb +10 -45
  52. data/lib/rubocop/cop/minitest/test_method_name.rb +70 -0
  53. data/lib/rubocop/cop/minitest/unspecified_exception.rb +36 -0
  54. data/lib/rubocop/cop/minitest_cops.rb +17 -1
  55. data/lib/rubocop/cop/mixin/argument_range_helper.rb +10 -0
  56. data/lib/rubocop/cop/mixin/in_delta_mixin.rb +50 -0
  57. data/lib/rubocop/cop/mixin/minitest_cop_rule.rb +102 -0
  58. data/lib/rubocop/cop/mixin/minitest_exploration_helpers.rb +84 -0
  59. data/lib/rubocop/minitest/version.rb +1 -1
  60. data/mkdocs.yml +2 -2
  61. data/relnotes/v0.10.0.md +21 -0
  62. data/relnotes/v0.6.2.md +5 -0
  63. data/relnotes/v0.7.0.md +13 -0
  64. data/relnotes/v0.8.0.md +12 -0
  65. data/relnotes/v0.8.1.md +5 -0
  66. data/relnotes/v0.9.0.md +10 -0
  67. data/rubocop-minitest.gemspec +4 -4
  68. data/tasks/cops_documentation.rake +83 -52
  69. data/tasks/cut_release.rake +16 -0
  70. metadata +45 -15
  71. data/lib/rubocop/cop/mixin/includes_cop_rule.rb +0 -78
@@ -9,51 +9,16 @@ module RuboCop
9
9
  # @example
10
10
  # # bad
11
11
  # refute(matcher.match(string))
12
- # refute(matcher.match(string), 'the message')
12
+ # refute(matcher.match(string), 'message')
13
13
  #
14
14
  # # good
15
15
  # refute_match(matcher, string)
16
- # refute_match(matcher, string, 'the message')
16
+ # refute_match(matcher, string, 'message')
17
17
  #
18
18
  class RefuteMatch < Cop
19
- include ArgumentRangeHelper
19
+ extend MinitestCopRule
20
20
 
21
- MSG = 'Prefer using `refute_match(%<arguments>s)` over ' \
22
- '`refute(%<receiver>s)`.'
23
-
24
- def_node_matcher :refute_with_match, <<~PATTERN
25
- (send nil? :refute $(send $_ :match $_) $...)
26
- PATTERN
27
-
28
- def on_send(node)
29
- refute_with_match(node) do
30
- |first_receiver_arg, matcher, actual, rest_receiver_arg|
31
- message = rest_receiver_arg.first
32
- arguments = node_arguments(matcher, actual, message)
33
- receiver = [first_receiver_arg.source, message&.source].compact.join(', ')
34
-
35
- offense_message = format(MSG, arguments: arguments, receiver: receiver)
36
-
37
- add_offense(node, message: offense_message)
38
- end
39
- end
40
-
41
- def autocorrect(node)
42
- lambda do |corrector|
43
- refute_with_match(node) do |_, matcher, actual|
44
- corrector.replace(node.loc.selector, 'refute_match')
45
-
46
- replacement = [matcher, actual].map(&:source).join(', ')
47
- corrector.replace(first_argument_range(node), replacement)
48
- end
49
- end
50
- end
51
-
52
- private
53
-
54
- def node_arguments(matcher, actual, message)
55
- [matcher.source, actual.source, message&.source].compact.join(', ')
56
- end
21
+ define_rule :refute, target_method: :match
57
22
  end
58
23
  end
59
24
  end
@@ -9,11 +9,11 @@ module RuboCop
9
9
  # @example
10
10
  # # bad
11
11
  # refute_equal(nil, actual)
12
- # refute_equal(nil, actual, 'the message')
12
+ # refute_equal(nil, actual, 'message')
13
13
  #
14
14
  # # good
15
15
  # refute_nil(actual)
16
- # refute_nil(actual, 'the message')
16
+ # refute_nil(actual, 'message')
17
17
  #
18
18
  class RefuteNil < Cop
19
19
  include ArgumentRangeHelper
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Minitest
6
+ # This cop enforces the test to use `refute_path_exists`
7
+ # instead of using `refute(File.exist?(path))`.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # refute(File.exist?(path))
12
+ # refute(File.exist?(path), 'message')
13
+ #
14
+ # # good
15
+ # refute_path_exists(path)
16
+ # refute_path_exists(path, 'message')
17
+ #
18
+ class RefutePathExists < Cop
19
+ MSG = 'Prefer using `%<good_method>s` over `%<bad_method>s`.'
20
+
21
+ def_node_matcher :refute_file_exists, <<~PATTERN
22
+ (send nil? :refute
23
+ (send
24
+ (const _ :File) {:exist? :exists?} $_)
25
+ $...)
26
+ PATTERN
27
+
28
+ def on_send(node)
29
+ refute_file_exists(node) do |path, failure_message|
30
+ failure_message = failure_message.first
31
+ good_method = build_good_method(path, failure_message)
32
+ message = format(MSG, good_method: good_method, bad_method: node.source)
33
+
34
+ add_offense(node, message: message)
35
+ end
36
+ end
37
+
38
+ def autocorrect(node)
39
+ refute_file_exists(node) do |path, failure_message|
40
+ failure_message = failure_message.first
41
+
42
+ lambda do |corrector|
43
+ replacement = build_good_method(path, failure_message)
44
+ corrector.replace(node, replacement)
45
+ end
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def build_good_method(path, message)
52
+ args = [path.source, message&.source].compact.join(', ')
53
+ "refute_path_exists(#{args})"
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -3,59 +3,24 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Minitest
6
- # This cop enforces the test to use `refute_respond_to(object, :some_method)`
7
- # over `refute(object.respond_to?(:some_method))`.
6
+ # This cop enforces the test to use `refute_respond_to(object, :do_something)`
7
+ # over `refute(object.respond_to?(:do_something))`.
8
8
  #
9
9
  # @example
10
10
  # # bad
11
- # refute(object.respond_to?(:some_method))
12
- # refute(object.respond_to?(:some_method), 'the message')
13
- # refute(respond_to?(:some_method))
11
+ # refute(object.respond_to?(:do_something))
12
+ # refute(object.respond_to?(:do_something), 'message')
13
+ # refute(respond_to?(:do_something))
14
14
  #
15
15
  # # good
16
- # refute_respond_to(object, :some_method)
17
- # refute_respond_to(object, :some_method, 'the message')
18
- # refute_respond_to(self, :some_method)
16
+ # refute_respond_to(object, :do_something)
17
+ # refute_respond_to(object, :do_something, 'message')
18
+ # refute_respond_to(self, :do_something)
19
19
  #
20
20
  class RefuteRespondTo < Cop
21
- include ArgumentRangeHelper
21
+ extend MinitestCopRule
22
22
 
23
- MSG = 'Prefer using `refute_respond_to(%<preferred>s)` over ' \
24
- '`refute(%<over>s)`.'
25
-
26
- def_node_matcher :refute_with_respond_to, <<~PATTERN
27
- (send nil? :refute $(send $_ :respond_to? $_) $...)
28
- PATTERN
29
-
30
- def on_send(node)
31
- refute_with_respond_to(node) do |over, object, method, rest_args|
32
- custom_message = rest_args.first
33
- preferred = build_preferred_arguments(object, method, custom_message)
34
- over = [over, custom_message].compact.map(&:source).join(', ')
35
- message = format(MSG, preferred: preferred, over: over)
36
- add_offense(node, message: message)
37
- end
38
- end
39
-
40
- def autocorrect(node)
41
- lambda do |corrector|
42
- refute_with_respond_to(node) do |_, object, method|
43
- corrector.replace(node.loc.selector, 'refute_respond_to')
44
-
45
- object = object ? object.source : 'self'
46
- replacement = [object, method.source].join(', ')
47
- corrector.replace(first_argument_range(node), replacement)
48
- end
49
- end
50
- end
51
-
52
- private
53
-
54
- def build_preferred_arguments(receiver, method, message)
55
- receiver = receiver ? receiver.source : 'self'
56
-
57
- [receiver, method.source, message&.source].compact.join(', ')
58
- end
23
+ define_rule :refute, target_method: :respond_to?
59
24
  end
60
25
  end
61
26
  end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Minitest
6
+ # This cop enforces that test method names start with `test_` prefix.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # class FooTest < Minitest::Test
11
+ # def does_something
12
+ # assert_equal 42, do_something
13
+ # end
14
+ # end
15
+ #
16
+ # # good
17
+ # class FooTest < Minitest::Test
18
+ # def test_does_something
19
+ # assert_equal 42, do_something
20
+ # end
21
+ # end
22
+ #
23
+ class TestMethodName < Cop
24
+ include MinitestExplorationHelpers
25
+ include DefNode
26
+
27
+ MSG = 'Test method name should start with `test_` prefix.'
28
+
29
+ def on_class(class_node)
30
+ return unless test_class?(class_node)
31
+
32
+ class_elements(class_node).each do |node|
33
+ add_offense(node, location: :name) if offense?(node)
34
+ end
35
+ end
36
+
37
+ def autocorrect(node)
38
+ lambda do |corrector|
39
+ corrector.replace(node.loc.name, "test_#{node.method_name}")
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def class_elements(class_node)
46
+ class_def = class_node.body
47
+ return [] unless class_def
48
+
49
+ if class_def.def_type?
50
+ [class_def]
51
+ else
52
+ class_def.each_child_node(:def).to_a
53
+ end
54
+ end
55
+
56
+ def offense?(node)
57
+ public?(node) && !test_method_name?(node) && !lifecycle_hook_method?(node)
58
+ end
59
+
60
+ def public?(node)
61
+ !non_public?(node)
62
+ end
63
+
64
+ def test_method_name?(node)
65
+ node.method_name.to_s.start_with?('test_')
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Minitest
6
+ # This cop checks for a specified error in `assert_raises`.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # assert_raises { raise FooException }
11
+ # assert_raises('This should have raised') { raise FooException }
12
+ #
13
+ # # good
14
+ # assert_raises(FooException) { raise FooException }
15
+ # assert_raises(FooException, 'This should have raised') { raise FooException }
16
+ #
17
+ class UnspecifiedException < Cop
18
+ MSG = 'Specify the exception being captured.'
19
+
20
+ def on_block(block_node)
21
+ node = block_node.send_node
22
+ return unless node.method?(:assert_raises)
23
+
24
+ add_offense(node) if unspecified_exception?(node)
25
+ end
26
+
27
+ private
28
+
29
+ def unspecified_exception?(node)
30
+ args = node.arguments
31
+ args.empty? || (args.size == 1 && args[0].str_type?)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,21 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'mixin/argument_range_helper'
4
- require_relative 'mixin/includes_cop_rule'
4
+ require_relative 'mixin/in_delta_mixin'
5
+ require_relative 'mixin/minitest_cop_rule'
6
+ require_relative 'mixin/minitest_exploration_helpers'
5
7
  require_relative 'minitest/assert_empty'
6
8
  require_relative 'minitest/assert_empty_literal'
7
9
  require_relative 'minitest/assert_equal'
10
+ require_relative 'minitest/assert_in_delta'
11
+ require_relative 'minitest/assertion_in_lifecycle_hook'
12
+ require_relative 'minitest/assert_kind_of'
8
13
  require_relative 'minitest/assert_nil'
9
14
  require_relative 'minitest/assert_includes'
10
15
  require_relative 'minitest/assert_instance_of'
11
16
  require_relative 'minitest/assert_match'
17
+ require_relative 'minitest/assert_output'
18
+ require_relative 'minitest/assert_path_exists'
12
19
  require_relative 'minitest/assert_respond_to'
20
+ require_relative 'minitest/assert_silent'
13
21
  require_relative 'minitest/assert_truthy'
22
+ require_relative 'minitest/global_expectations'
23
+ require_relative 'minitest/literal_as_actual_argument'
24
+ require_relative 'minitest/multiple_assertions'
14
25
  require_relative 'minitest/refute_empty'
15
26
  require_relative 'minitest/refute_false'
16
27
  require_relative 'minitest/refute_equal'
28
+ require_relative 'minitest/refute_in_delta'
29
+ require_relative 'minitest/refute_kind_of'
17
30
  require_relative 'minitest/refute_nil'
18
31
  require_relative 'minitest/refute_includes'
19
32
  require_relative 'minitest/refute_match'
20
33
  require_relative 'minitest/refute_instance_of'
34
+ require_relative 'minitest/refute_path_exists'
21
35
  require_relative 'minitest/refute_respond_to'
36
+ require_relative 'minitest/test_method_name'
37
+ require_relative 'minitest/unspecified_exception'
@@ -26,6 +26,16 @@ module RuboCop
26
26
  second_argument.source_range.end_pos
27
27
  )
28
28
  end
29
+
30
+ def all_arguments_range(node)
31
+ first_argument = node.first_argument
32
+ last_argument = node.arguments.last
33
+
34
+ range_between(
35
+ first_argument.source_range.begin_pos,
36
+ last_argument.source_range.end_pos
37
+ )
38
+ end
29
39
  end
30
40
  end
31
41
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Common functionality for `AssertInDelta` and `RefuteInDelta` cops.
6
+ module InDeltaMixin
7
+ MSG = 'Prefer using `%<good_method>s` over `%<bad_method>s`.'
8
+
9
+ def on_send(node)
10
+ equal_floats_call(node) do |expected, actual, message|
11
+ message = message.first
12
+
13
+ if expected.float_type? || actual.float_type?
14
+ message = format(MSG,
15
+ good_method: build_good_method(expected, actual, message),
16
+ bad_method: node.source)
17
+
18
+ add_offense(node, message: message)
19
+ end
20
+ end
21
+ end
22
+
23
+ def autocorrect(node)
24
+ equal_floats_call(node) do |expected, actual, message|
25
+ message = message.first
26
+ replacement = build_good_method(expected, actual, message)
27
+
28
+ lambda do |corrector|
29
+ corrector.replace(node, replacement)
30
+ end
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def build_good_method(expected, actual, message)
37
+ if message
38
+ "#{assertion_method}_in_delta(#{expected.source}, #{actual.source}, 0.001, #{message.source})"
39
+ else
40
+ "#{assertion_method}_in_delta(#{expected.source}, #{actual.source})"
41
+ end
42
+ end
43
+
44
+ def assertion_method
45
+ class_name = self.class.name.split('::').last
46
+ class_name[/\A[[:upper:]][[:lower:]]+/].downcase
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Provide a method to define offense rule for Minitest cops.
6
+ module MinitestCopRule
7
+ #
8
+ # Define offense rule for Minitest cops.
9
+ #
10
+ # @example
11
+ # define_rule :assert, target_method: :match
12
+ # define_rule :refute, target_method: :match
13
+ # define_rule :assert, target_method: :include?, preferred_method: :assert_includes
14
+ # define_rule :assert, target_method: :instance_of?, inverse: true
15
+ #
16
+ # @param assertion_method [Symbol] Assertion method like `assert` or `refute`.
17
+ # @param target_method [Symbol] Method name offensed by assertion method arguments.
18
+ # @param preferred_method [Symbol] An optional param. Custom method name replaced by
19
+ # auto-correction. The preferred method name that connects
20
+ # `assertion_method` and `target_method` with `_` is
21
+ # the default name.
22
+ # @param inverse [Boolean] An optional param. Order of arguments replaced by auto-correction.
23
+ #
24
+ def define_rule(assertion_method, target_method:, preferred_method: nil, inverse: false)
25
+ preferred_method = "#{assertion_method}_#{target_method.to_s.delete('?')}" if preferred_method.nil?
26
+
27
+ class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
28
+ include ArgumentRangeHelper
29
+
30
+ MSG = 'Prefer using `#{preferred_method}(%<new_arguments>s)` over ' \
31
+ '`#{assertion_method}(%<original_arguments>s)`.'
32
+
33
+ def on_send(node)
34
+ return unless node.method?(:#{assertion_method})
35
+ return unless (arguments = peel_redundant_parentheses_from(node.arguments))
36
+ return unless arguments.first.respond_to?(:method?) && arguments.first.method?(:#{target_method})
37
+
38
+ add_offense(node, message: offense_message(arguments))
39
+ end
40
+
41
+ def autocorrect(node)
42
+ lambda do |corrector|
43
+ corrector.replace(node.loc.selector, '#{preferred_method}')
44
+
45
+ arguments = peel_redundant_parentheses_from(node.arguments)
46
+
47
+ new_arguments = new_arguments(arguments).join(', ')
48
+
49
+ if enclosed_in_redundant_parentheses?(node)
50
+ new_arguments = '(' + new_arguments + ')'
51
+ end
52
+
53
+ corrector.replace(first_argument_range(node), new_arguments)
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def peel_redundant_parentheses_from(arguments)
60
+ return arguments unless arguments.first.begin_type?
61
+
62
+ peel_redundant_parentheses_from(arguments.first.children)
63
+ end
64
+
65
+ def offense_message(arguments)
66
+ message_argument = arguments.last if arguments.first != arguments.last
67
+
68
+ new_arguments = [
69
+ new_arguments(arguments),
70
+ message_argument&.source
71
+ ].flatten.compact.join(', ')
72
+
73
+ original_arguments = arguments.map(&:source).join(', ')
74
+
75
+ format(
76
+ MSG,
77
+ new_arguments: new_arguments,
78
+ original_arguments: original_arguments
79
+ )
80
+ end
81
+
82
+ def new_arguments(arguments)
83
+ receiver = correct_receiver(arguments.first.receiver)
84
+ method_argument = arguments.first.arguments.first&.source
85
+
86
+ new_arguments = [receiver, method_argument].compact
87
+ new_arguments.reverse! if #{inverse}
88
+ new_arguments
89
+ end
90
+
91
+ def enclosed_in_redundant_parentheses?(node)
92
+ node.arguments.first.begin_type?
93
+ end
94
+
95
+ def correct_receiver(receiver)
96
+ receiver ? receiver.source : 'self'
97
+ end
98
+ RUBY
99
+ end
100
+ end
101
+ end
102
+ end