rubocop-minitest 0.7.0 → 0.10.1

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +0 -3
  3. data/.rubocop.yml +2 -1
  4. data/.rubocop_todo.yml +0 -7
  5. data/CHANGELOG.md +58 -0
  6. data/Gemfile +1 -1
  7. data/README.md +2 -2
  8. data/Rakefile +29 -0
  9. data/config/default.yml +96 -16
  10. data/docs/antora.yml +7 -0
  11. data/docs/modules/ROOT/nav.adoc +6 -0
  12. data/docs/modules/ROOT/pages/cops.adoc +37 -0
  13. data/docs/modules/ROOT/pages/cops_minitest.adoc +1014 -0
  14. data/docs/modules/ROOT/pages/index.adoc +5 -0
  15. data/docs/modules/ROOT/pages/installation.adoc +15 -0
  16. data/docs/modules/ROOT/pages/usage.adoc +32 -0
  17. data/{manual → legacy-docs}/cops.md +0 -0
  18. data/{manual → legacy-docs}/cops_minitest.md +7 -5
  19. data/{manual → legacy-docs}/index.md +0 -0
  20. data/{manual → legacy-docs}/installation.md +0 -0
  21. data/{manual → legacy-docs}/usage.md +0 -0
  22. data/lib/rubocop/cop/generator.rb +56 -0
  23. data/lib/rubocop/cop/minitest/assert_empty_literal.rb +15 -0
  24. data/lib/rubocop/cop/minitest/assert_equal.rb +2 -31
  25. data/lib/rubocop/cop/minitest/assert_in_delta.rb +27 -0
  26. data/lib/rubocop/cop/minitest/assert_kind_of.rb +25 -0
  27. data/lib/rubocop/cop/minitest/assert_output.rb +49 -0
  28. data/lib/rubocop/cop/minitest/assert_path_exists.rb +58 -0
  29. data/lib/rubocop/cop/minitest/assert_silent.rb +45 -0
  30. data/lib/rubocop/cop/minitest/assertion_in_lifecycle_hook.rb +43 -0
  31. data/lib/rubocop/cop/minitest/global_expectations.rb +49 -28
  32. data/lib/rubocop/cop/minitest/literal_as_actual_argument.rb +52 -0
  33. data/lib/rubocop/cop/minitest/multiple_assertions.rb +63 -0
  34. data/lib/rubocop/cop/minitest/refute_in_delta.rb +27 -0
  35. data/lib/rubocop/cop/minitest/refute_kind_of.rb +25 -0
  36. data/lib/rubocop/cop/minitest/refute_path_exists.rb +58 -0
  37. data/lib/rubocop/cop/minitest/test_method_name.rb +70 -0
  38. data/lib/rubocop/cop/minitest/unspecified_exception.rb +36 -0
  39. data/lib/rubocop/cop/minitest_cops.rb +15 -0
  40. data/lib/rubocop/cop/mixin/argument_range_helper.rb +10 -0
  41. data/lib/rubocop/cop/mixin/in_delta_mixin.rb +50 -0
  42. data/lib/rubocop/cop/mixin/minitest_cop_rule.rb +1 -3
  43. data/lib/rubocop/cop/mixin/minitest_exploration_helpers.rb +84 -0
  44. data/lib/rubocop/minitest/version.rb +1 -1
  45. data/mkdocs.yml +2 -2
  46. data/relnotes/v0.10.0.md +21 -0
  47. data/relnotes/v0.10.1.md +5 -0
  48. data/relnotes/v0.8.0.md +12 -0
  49. data/relnotes/v0.8.1.md +5 -0
  50. data/relnotes/v0.9.0.md +10 -0
  51. data/rubocop-minitest.gemspec +4 -4
  52. data/tasks/cops_documentation.rake +83 -54
  53. data/tasks/cut_release.rake +16 -0
  54. metadata +40 -12
@@ -0,0 +1,5 @@
1
+ = RuboCop Minitest
2
+
3
+ A https://github.com/rubocop-hq/rubocop[RuboCop] extension focused on enforcing Minitest best practices and coding conventions.
4
+
5
+ It's based on the community-driven https://minitest.rubystyle.guide[Minitest style guide].
@@ -0,0 +1,15 @@
1
+ = Installation
2
+
3
+ Just install the `rubocop-minitest` gem
4
+
5
+ [source,sh]
6
+ ----
7
+ gem install rubocop-minitest
8
+ ----
9
+
10
+ or if you use bundler put this in your `Gemfile`
11
+
12
+ [source,ruby]
13
+ ----
14
+ gem 'rubocop-minitest'
15
+ ----
@@ -0,0 +1,32 @@
1
+ = Usage
2
+
3
+ You need to tell RuboCop to load the Minitest extension. There are three
4
+ ways to do this:
5
+
6
+ == RuboCop configuration file
7
+
8
+ Put this into your `.rubocop.yml`.
9
+
10
+ [source,yaml]
11
+ ----
12
+ require: rubocop-minitest
13
+ ----
14
+
15
+ Now you can run `rubocop` and it will automatically load the RuboCop Minitest
16
+ cops together with the standard cops.
17
+
18
+ == Command line
19
+
20
+ [source,sh]
21
+ ----
22
+ rubocop --require rubocop-minitest
23
+ ----
24
+
25
+ == Rake task
26
+
27
+ [source,ruby]
28
+ ----
29
+ RuboCop::RakeTask.new do |task|
30
+ task.requires << 'rubocop-minitest'
31
+ end
32
+ ----
File without changes
@@ -226,19 +226,21 @@ Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChan
226
226
  --- | --- | --- | --- | ---
227
227
  Enabled | Yes | Yes | 0.7 | -
228
228
 
229
- This Cop checks for deprecated global expectations
229
+ This cop checks for deprecated global expectations
230
230
  and autocorrects them to use expect format.
231
231
 
232
232
  ### Examples
233
233
 
234
234
  ```ruby
235
235
  # bad
236
- n.must_equal 42
237
- n.wont_match b
236
+ musts.must_equal expected_musts
237
+ wonts.wont_match expected_wonts
238
+ musts.must_raise TypeError
238
239
 
239
240
  # good
240
- _(n).must_equal 42
241
- _(n).wont_match b
241
+ _(musts).must_equal expected_musts
242
+ _(wonts).wont_match expected_wonts
243
+ _ { musts }.must_raise TypeError
242
244
  ```
243
245
 
244
246
  ## Minitest/RefuteEmpty
File without changes
File without changes
File without changes
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Source and test generator for new cops
6
+ #
7
+ # This generator will take a cop name and generate a source file
8
+ # and test file when given a valid qualified cop name.
9
+ class Generator
10
+ TEST_TEMPLATE = <<~TEST
11
+ # frozen_string_literal: true
12
+
13
+ require 'test_helper'
14
+
15
+ class %<cop_name>sTest < Minitest::Test
16
+ def test_registers_offense_when_using_bad_method
17
+ assert_offense(<<~RUBY)
18
+ bad_method
19
+ ^^^^^^^^^^ Use `#good_method` instead of `#bad_method`.
20
+ RUBY
21
+
22
+ assert_correction(<<~RUBY)
23
+ good_method
24
+ RUBY
25
+ end
26
+
27
+ def test_does_not_register_offense_when_using_good_method
28
+ assert_no_offenses(<<~RUBY)
29
+ good_method
30
+ RUBY
31
+ end
32
+ end
33
+ TEST
34
+
35
+ def write_test
36
+ write_unless_file_exists(test_path, generated_test)
37
+ end
38
+
39
+ private
40
+
41
+ def test_path
42
+ File.join(
43
+ 'test',
44
+ 'rubocop',
45
+ 'cop',
46
+ 'minitest',
47
+ "#{snake_case(badge.cop_name.to_s)}_test.rb"
48
+ )
49
+ end
50
+
51
+ def generated_test
52
+ generate(TEST_TEMPLATE)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -15,6 +15,8 @@ module RuboCop
15
15
  # assert_empty(object)
16
16
  #
17
17
  class AssertEmptyLiteral < Cop
18
+ include ArgumentRangeHelper
19
+
18
20
  MSG = 'Prefer using `assert_empty(%<arguments>s)` over ' \
19
21
  '`assert(%<literal>s, %<arguments>s)`.'
20
22
 
@@ -24,12 +26,25 @@ module RuboCop
24
26
 
25
27
  def on_send(node)
26
28
  assert_with_empty_literal(node) do |literal, matchers|
29
+ return unless literal.values.empty?
30
+
27
31
  args = matchers.map(&:source).join(', ')
28
32
 
29
33
  message = format(MSG, literal: literal.source, arguments: args)
30
34
  add_offense(node, message: message)
31
35
  end
32
36
  end
37
+
38
+ def autocorrect(node)
39
+ assert_with_empty_literal(node) do |_literal, matchers|
40
+ object = matchers.first
41
+
42
+ lambda do |corrector|
43
+ corrector.replace(node.loc.selector, 'assert_empty')
44
+ corrector.replace(first_and_second_arguments_range(node), object.source)
45
+ end
46
+ end
47
+ end
33
48
  end
34
49
  end
35
50
  end
@@ -14,38 +14,9 @@ module RuboCop
14
14
  # assert_equal("rubocop-minitest", actual)
15
15
  #
16
16
  class AssertEqual < Cop
17
- include ArgumentRangeHelper
17
+ extend MinitestCopRule
18
18
 
19
- MSG = 'Prefer using `assert_equal(%<preferred>s)` over ' \
20
- '`assert(%<over>s)`.'
21
-
22
- def_node_matcher :assert_equal, <<~PATTERN
23
- (send nil? :assert $(send $_ :== $_) $...)
24
- PATTERN
25
-
26
- def on_send(node)
27
- assert_equal(node) do |first_receiver_arg, expected, actual, rest_receiver_arg|
28
- message = rest_receiver_arg.first
29
- preferred = [expected.source, actual.source, message&.source]
30
- .compact.join(', ')
31
- over = [first_receiver_arg.source, message&.source].compact.join(', ')
32
-
33
- offense_message = format(MSG, preferred: preferred, over: over)
34
-
35
- add_offense(node, message: offense_message)
36
- end
37
- end
38
-
39
- def autocorrect(node)
40
- lambda do |corrector|
41
- assert_equal(node) do |_, expected, actual|
42
- corrector.replace(node.loc.selector, 'assert_equal')
43
-
44
- replacement = [expected, actual].map(&:source).join(', ')
45
- corrector.replace(first_argument_range(node), replacement)
46
- end
47
- end
48
- end
19
+ define_rule :assert, target_method: :==, preferred_method: :assert_equal
49
20
  end
50
21
  end
51
22
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Minitest
6
+ # This cop enforces the test to use `assert_in_delta`
7
+ # instead of using `assert_equal` to compare floats.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # assert_equal(0.2, actual)
12
+ # assert_equal(0.2, actual, 'message')
13
+ #
14
+ # # good
15
+ # assert_in_delta(0.2, actual)
16
+ # assert_in_delta(0.2, actual, 0.001, 'message')
17
+ #
18
+ class AssertInDelta < Cop
19
+ include InDeltaMixin
20
+
21
+ def_node_matcher :equal_floats_call, <<~PATTERN
22
+ (send nil? :assert_equal $_ $_ $...)
23
+ PATTERN
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Minitest
6
+ # This cop enforces the test to use `assert_kind_of(Class, object)`
7
+ # over `assert(object.kind_of?(Class))`.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # assert(object.kind_of?(Class))
12
+ # assert(object.kind_of?(Class), 'message')
13
+ #
14
+ # # good
15
+ # assert_kind_of(Class, object)
16
+ # assert_kind_of(Class, object, 'message')
17
+ #
18
+ class AssertKindOf < Cop
19
+ extend MinitestCopRule
20
+
21
+ define_rule :assert, target_method: :kind_of?, inverse: true
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Minitest
6
+ # This cop checks for opportunities to use `assert_output`.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # $stdout = StringIO.new
11
+ # puts object.method
12
+ # $stdout.rewind
13
+ # assert_match expected, $stdout.read
14
+ #
15
+ # # good
16
+ # assert_output(expected) { puts object.method }
17
+ #
18
+ class AssertOutput < Cop
19
+ include MinitestExplorationHelpers
20
+
21
+ MSG = 'Use `assert_output` instead of mutating %<name>s.'
22
+ OUTPUT_GLOBAL_VARIABLES = %i[$stdout $stderr].freeze
23
+
24
+ def on_gvasgn(node)
25
+ test_case_node = find_test_case(node)
26
+ return unless test_case_node
27
+
28
+ gvar_name = node.children.first
29
+ return unless OUTPUT_GLOBAL_VARIABLES.include?(gvar_name)
30
+
31
+ assertions(test_case_node).each do |assertion|
32
+ add_offense(assertion, message: format(MSG, name: gvar_name)) if references_gvar?(assertion, gvar_name)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def find_test_case(node)
39
+ def_ancestor = node.each_ancestor(:def).first
40
+ def_ancestor if test_case?(def_ancestor)
41
+ end
42
+
43
+ def references_gvar?(assertion, gvar_name)
44
+ assertion.each_descendant(:gvar).any? { |d| d.children.first == gvar_name }
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -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 `assert_path_exists`
7
+ # instead of using `assert(File.exist?(path))`.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # assert(File.exist?(path))
12
+ # assert(File.exist?(path), 'message')
13
+ #
14
+ # # good
15
+ # assert_path_exists(path)
16
+ # assert_path_exists(path, 'message')
17
+ #
18
+ class AssertPathExists < Cop
19
+ MSG = 'Prefer using `%<good_method>s` over `%<bad_method>s`.'
20
+
21
+ def_node_matcher :assert_file_exists, <<~PATTERN
22
+ (send nil? :assert
23
+ (send
24
+ (const _ :File) {:exist? :exists?} $_)
25
+ $...)
26
+ PATTERN
27
+
28
+ def on_send(node)
29
+ assert_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
+ assert_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
+ "assert_path_exists(#{args})"
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Minitest
6
+ # This cop enforces the test to use `assert_silent { ... }`
7
+ # instead of using `assert_output('', '') { ... }`.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # assert_output('', '') { puts object.do_something }
12
+ #
13
+ # # good
14
+ # assert_silent { puts object.do_something }
15
+ #
16
+ class AssertSilent < Cop
17
+ MSG = 'Prefer using `assert_silent` over `assert_output("", "")`.'
18
+
19
+ def_node_matcher :assert_silent_candidate?, <<~PATTERN
20
+ (block
21
+ (send nil? :assert_output
22
+ #empty_string?
23
+ #empty_string?)
24
+ ...)
25
+ PATTERN
26
+
27
+ def on_block(node)
28
+ add_offense(node.send_node) if assert_silent_candidate?(node)
29
+ end
30
+
31
+ def autocorrect(node)
32
+ lambda do |corrector|
33
+ corrector.replace(node, 'assert_silent')
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def empty_string?(node)
40
+ node.str_type? && node.value.empty?
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Minitest
6
+ # This cop checks for usage of assertions in lifecycle hooks.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # class FooTest < Minitest::Test
11
+ # def setup
12
+ # assert_equal(foo, bar)
13
+ # end
14
+ # end
15
+ #
16
+ # # good
17
+ # class FooTest < Minitest::Test
18
+ # def test_something
19
+ # assert_equal(foo, bar)
20
+ # end
21
+ # end
22
+ #
23
+ class AssertionInLifecycleHook < Cop
24
+ include MinitestExplorationHelpers
25
+
26
+ MSG = 'Do not use `%<assertion>s` in `%<hook>s` hook.'
27
+
28
+ def on_class(class_node)
29
+ return unless test_class?(class_node)
30
+
31
+ lifecycle_hooks(class_node).each do |hook_node|
32
+ hook_node.each_descendant(:send) do |node|
33
+ if assertion?(node)
34
+ message = format(MSG, assertion: node.method_name, hook: hook_node.method_name)
35
+ add_offense(node, message: message)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end