rubocop-minitest 0.22.2 → 0.32.2
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.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +5 -5
- data/config/default.yml +82 -1
- data/lib/rubocop/cop/generator.rb +1 -1
- data/lib/rubocop/cop/minitest/assert_instance_of.rb +18 -2
- data/lib/rubocop/cop/minitest/assert_match.rb +4 -1
- data/lib/rubocop/cop/minitest/assert_operator.rb +58 -0
- data/lib/rubocop/cop/minitest/assert_output.rb +1 -2
- data/lib/rubocop/cop/minitest/assert_path_exists.rb +9 -4
- data/lib/rubocop/cop/minitest/assert_same.rb +26 -0
- data/lib/rubocop/cop/minitest/assert_truthy.rb +10 -0
- data/lib/rubocop/cop/minitest/assert_with_expected_argument.rb +7 -1
- data/lib/rubocop/cop/minitest/empty_line_before_assertion_methods.rb +101 -0
- data/lib/rubocop/cop/minitest/global_expectations.rb +3 -1
- data/lib/rubocop/cop/minitest/lifecycle_hooks_order.rb +100 -0
- data/lib/rubocop/cop/minitest/literal_as_actual_argument.rb +16 -5
- data/lib/rubocop/cop/minitest/multiple_assertions.rb +23 -4
- data/lib/rubocop/cop/minitest/no_assertions.rb +1 -8
- data/lib/rubocop/cop/minitest/no_test_cases.rb +35 -0
- data/lib/rubocop/cop/minitest/non_public_test_method.rb +55 -0
- data/lib/rubocop/cop/minitest/refute_equal.rb +2 -3
- data/lib/rubocop/cop/minitest/refute_false.rb +11 -1
- data/lib/rubocop/cop/minitest/refute_instance_of.rb +18 -2
- data/lib/rubocop/cop/minitest/refute_match.rb +4 -1
- data/lib/rubocop/cop/minitest/refute_operator.rb +58 -0
- data/lib/rubocop/cop/minitest/refute_path_exists.rb +9 -4
- data/lib/rubocop/cop/minitest/refute_same.rb +26 -0
- data/lib/rubocop/cop/minitest/return_in_test_method.rb +44 -0
- data/lib/rubocop/cop/minitest/skip_without_reason.rb +66 -0
- data/lib/rubocop/cop/minitest/test_file_name.rb +46 -0
- data/lib/rubocop/cop/minitest/test_method_name.rb +1 -12
- data/lib/rubocop/cop/minitest/useless_assertion.rb +75 -0
- data/lib/rubocop/cop/minitest_cops.rb +16 -3
- data/lib/rubocop/cop/mixin/argument_range_helper.rb +0 -6
- data/lib/rubocop/cop/mixin/instance_of_assertion_handleable.rb +48 -0
- data/lib/rubocop/cop/mixin/minitest_cop_rule.rb +16 -5
- data/lib/rubocop/cop/mixin/minitest_exploration_helpers.rb +32 -11
- data/lib/rubocop/cop/mixin/predicate_assertion_handleable.rb +1 -1
- data/lib/rubocop/minitest/assert_offense.rb +47 -6
- data/lib/rubocop/minitest/version.rb +1 -1
- metadata +20 -21
@@ -27,24 +27,35 @@ module RuboCop
|
|
27
27
|
def on_send(node)
|
28
28
|
return unless node.method?(:assert_equal)
|
29
29
|
|
30
|
-
expected, actual,
|
30
|
+
expected, actual, _message = *node.arguments
|
31
31
|
return unless actual&.recursive_basic_literal?
|
32
32
|
return if expected.recursive_basic_literal?
|
33
33
|
|
34
34
|
add_offense(all_arguments_range(node)) do |corrector|
|
35
|
-
autocorrect(corrector, node, expected, actual
|
35
|
+
autocorrect(corrector, node, expected, actual)
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
|
39
|
+
private
|
40
|
+
|
41
|
+
def autocorrect(corrector, node, expected, actual)
|
40
42
|
new_actual_source = if actual.hash_type? && !actual.braces?
|
41
43
|
"{#{actual.source}}"
|
42
44
|
else
|
43
45
|
actual.source
|
44
46
|
end
|
45
|
-
arguments = [new_actual_source, expected.source, message&.source].compact.join(', ')
|
46
47
|
|
47
|
-
corrector.replace(
|
48
|
+
corrector.replace(expected, new_actual_source)
|
49
|
+
corrector.replace(actual, expected.source)
|
50
|
+
|
51
|
+
wrap_with_parentheses(corrector, node) if !node.parenthesized? && actual.hash_type?
|
52
|
+
end
|
53
|
+
|
54
|
+
def wrap_with_parentheses(corrector, node)
|
55
|
+
range = node.loc.selector.end.join(node.first_argument.source_range.begin)
|
56
|
+
|
57
|
+
corrector.replace(range, '(')
|
58
|
+
corrector.insert_after(node, ')')
|
48
59
|
end
|
49
60
|
end
|
50
61
|
end
|
@@ -3,7 +3,8 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Minitest
|
6
|
-
# Checks if test cases contain too many assertion calls.
|
6
|
+
# Checks if test cases contain too many assertion calls. If conditional code with assertions
|
7
|
+
# is used, the branch with maximum assertions is counted.
|
7
8
|
# The maximum allowed assertion calls is configurable.
|
8
9
|
#
|
9
10
|
# @example Max: 1
|
@@ -36,7 +37,7 @@ module RuboCop
|
|
36
37
|
return unless test_class?(class_node)
|
37
38
|
|
38
39
|
test_cases(class_node).each do |node|
|
39
|
-
assertions_count = assertions_count(node)
|
40
|
+
assertions_count = assertions_count(node.body)
|
40
41
|
|
41
42
|
next unless assertions_count > max_assertions
|
42
43
|
|
@@ -50,8 +51,26 @@ module RuboCop
|
|
50
51
|
private
|
51
52
|
|
52
53
|
def assertions_count(node)
|
53
|
-
|
54
|
-
|
54
|
+
return 0 unless node.is_a?(RuboCop::AST::Node)
|
55
|
+
|
56
|
+
assertions =
|
57
|
+
case node.type
|
58
|
+
when :if, :case, :case_match
|
59
|
+
assertions_count_in_branches(node.branches)
|
60
|
+
when :rescue
|
61
|
+
assertions_count(node.body) + assertions_count_in_branches(node.branches)
|
62
|
+
when :block, :numblock
|
63
|
+
assertions_count(node.body)
|
64
|
+
else
|
65
|
+
node.each_child_node.sum { |child| assertions_count(child) }
|
66
|
+
end
|
67
|
+
|
68
|
+
assertions += 1 if assertion_method?(node)
|
69
|
+
assertions
|
70
|
+
end
|
71
|
+
|
72
|
+
def assertions_count_in_branches(branches)
|
73
|
+
branches.map { |branch| assertions_count(branch) }.max
|
55
74
|
end
|
56
75
|
|
57
76
|
def max_assertions
|
@@ -32,16 +32,9 @@ module RuboCop
|
|
32
32
|
|
33
33
|
next if assertions_count.positive?
|
34
34
|
|
35
|
-
add_offense(node.block_type? ? node.
|
35
|
+
add_offense(node.block_type? ? node.source_range : node.loc.name)
|
36
36
|
end
|
37
37
|
end
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
def assertions_count(node)
|
42
|
-
base = assertion_method?(node) ? 1 : 0
|
43
|
-
base + node.each_child_node.sum { |c| assertions_count(c) }
|
44
|
-
end
|
45
38
|
end
|
46
39
|
end
|
47
40
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Minitest
|
6
|
+
# Checks if test class contains any test cases.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# class FooTest < Minitest::Test
|
11
|
+
# def do_something
|
12
|
+
# end
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# class FooTest < Minitest::Test
|
17
|
+
# def test_something
|
18
|
+
# assert true
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
class NoTestCases < Base
|
23
|
+
include MinitestExplorationHelpers
|
24
|
+
|
25
|
+
MSG = 'Test class should have test cases.'
|
26
|
+
|
27
|
+
def on_class(node)
|
28
|
+
return unless test_class?(node)
|
29
|
+
|
30
|
+
add_offense(node) if test_cases(node).empty?
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Minitest
|
6
|
+
# Detects non `public` (marked as `private` or `protected`) test methods.
|
7
|
+
# Minitest runs only test methods which are `public`.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# class FooTest
|
12
|
+
# private # or protected
|
13
|
+
# def test_does_something
|
14
|
+
# assert_equal 42, do_something
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# # good
|
19
|
+
# class FooTest
|
20
|
+
# def test_does_something
|
21
|
+
# assert_equal 42, do_something
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# # good (not a test case name)
|
26
|
+
# class FooTest
|
27
|
+
# private # or protected
|
28
|
+
# def does_something
|
29
|
+
# assert_equal 42, do_something
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# # good (no assertions)
|
34
|
+
# class FooTest
|
35
|
+
# private # or protected
|
36
|
+
# def test_does_something
|
37
|
+
# do_something
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
class NonPublicTestMethod < Base
|
42
|
+
include MinitestExplorationHelpers
|
43
|
+
include DefNode
|
44
|
+
|
45
|
+
MSG = 'Non `public` test method detected. Make it `public` for it to run.'
|
46
|
+
|
47
|
+
def on_class(node)
|
48
|
+
test_cases(node, visibility_check: false).each do |test_case|
|
49
|
+
add_offense(test_case) if non_public?(test_case) && assertions(test_case).any?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -9,7 +9,6 @@ module RuboCop
|
|
9
9
|
# @example
|
10
10
|
# # bad
|
11
11
|
# assert("rubocop-minitest" != actual)
|
12
|
-
# assert(! "rubocop-minitest" == actual)
|
13
12
|
#
|
14
13
|
# # good
|
15
14
|
# refute_equal("rubocop-minitest", actual)
|
@@ -22,7 +21,7 @@ module RuboCop
|
|
22
21
|
RESTRICT_ON_SEND = %i[assert].freeze
|
23
22
|
|
24
23
|
def_node_matcher :assert_not_equal, <<~PATTERN
|
25
|
-
(send nil? :assert
|
24
|
+
(send nil? :assert (send $_ :!= $_) $... )
|
26
25
|
PATTERN
|
27
26
|
|
28
27
|
def on_send(node)
|
@@ -36,7 +35,7 @@ module RuboCop
|
|
36
35
|
corrector.replace(node.loc.selector, 'refute_equal')
|
37
36
|
|
38
37
|
replacement = [expected, actual].map(&:source).join(', ')
|
39
|
-
corrector.replace(
|
38
|
+
corrector.replace(node.first_argument, replacement)
|
40
39
|
end
|
41
40
|
end
|
42
41
|
end
|
@@ -5,6 +5,16 @@ module RuboCop
|
|
5
5
|
module Minitest
|
6
6
|
# Enforces the use of `refute(object)` over `assert_equal(false, object)`.
|
7
7
|
#
|
8
|
+
# @safety
|
9
|
+
# This cop is unsafe because it cannot detect failure when second argument is `nil`.
|
10
|
+
# False positives cannot be prevented when this is a variable or method return value.
|
11
|
+
#
|
12
|
+
# [source,ruby]
|
13
|
+
# ----
|
14
|
+
# assert_equal(false, nil) # failure
|
15
|
+
# refute(nil) # success
|
16
|
+
# ----
|
17
|
+
#
|
8
18
|
# @example
|
9
19
|
# # bad
|
10
20
|
# assert_equal(false, actual)
|
@@ -56,7 +66,7 @@ module RuboCop
|
|
56
66
|
if node.method?(:assert_equal)
|
57
67
|
corrector.replace(first_and_second_arguments_range(node), actual.source)
|
58
68
|
else
|
59
|
-
corrector.replace(
|
69
|
+
corrector.replace(node.first_argument, actual.source)
|
60
70
|
end
|
61
71
|
end
|
62
72
|
end
|
@@ -11,14 +11,30 @@ module RuboCop
|
|
11
11
|
# refute(object.instance_of?(Class))
|
12
12
|
# refute(object.instance_of?(Class), 'message')
|
13
13
|
#
|
14
|
+
# # bad
|
15
|
+
# refute_equal(Class, object.class)
|
16
|
+
# refute_equal(Class, object.class, 'message')
|
17
|
+
#
|
14
18
|
# # good
|
15
19
|
# refute_instance_of(Class, object)
|
16
20
|
# refute_instance_of(Class, object, 'message')
|
17
21
|
#
|
18
22
|
class RefuteInstanceOf < Base
|
19
|
-
|
23
|
+
include InstanceOfAssertionHandleable
|
24
|
+
extend AutoCorrector
|
25
|
+
|
26
|
+
RESTRICT_ON_SEND = %i[refute refute_equal].freeze
|
27
|
+
|
28
|
+
def_node_matcher :instance_of_assertion?, <<~PATTERN
|
29
|
+
{
|
30
|
+
(send nil? :refute (send $_ :instance_of? $const) $_?)
|
31
|
+
(send nil? :refute_equal $const (send $_ :class) $_?)
|
32
|
+
}
|
33
|
+
PATTERN
|
20
34
|
|
21
|
-
|
35
|
+
def on_send(node)
|
36
|
+
investigate(node, :refute)
|
37
|
+
end
|
22
38
|
end
|
23
39
|
end
|
24
40
|
end
|
@@ -9,6 +9,8 @@ module RuboCop
|
|
9
9
|
# @example
|
10
10
|
# # bad
|
11
11
|
# refute(matcher.match(string))
|
12
|
+
# refute(matcher.match?(string))
|
13
|
+
# refute(matcher =~ string)
|
12
14
|
# refute(matcher.match(string), 'message')
|
13
15
|
#
|
14
16
|
# # good
|
@@ -18,7 +20,8 @@ module RuboCop
|
|
18
20
|
class RefuteMatch < Base
|
19
21
|
extend MinitestCopRule
|
20
22
|
|
21
|
-
define_rule :refute, target_method:
|
23
|
+
define_rule :refute, target_method: %i[match match? =~],
|
24
|
+
preferred_method: :refute_match, inverse: 'regexp_type?'
|
22
25
|
end
|
23
26
|
end
|
24
27
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Minitest
|
6
|
+
# Enforces the use of `refute_operator(expected, :<, actual)` over `refute(expected < actual)`.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
#
|
10
|
+
# # bad
|
11
|
+
# refute(expected < actual)
|
12
|
+
#
|
13
|
+
# # good
|
14
|
+
# refute_operator(expected, :<, actual)
|
15
|
+
#
|
16
|
+
class RefuteOperator < Base
|
17
|
+
extend AutoCorrector
|
18
|
+
|
19
|
+
MSG = 'Prefer using `refute_operator(%<new_arguments>s)`.'
|
20
|
+
RESTRICT_ON_SEND = %i[refute].freeze
|
21
|
+
ALLOWED_OPERATORS = [:[]].freeze
|
22
|
+
|
23
|
+
def on_send(node)
|
24
|
+
first_argument = node.first_argument
|
25
|
+
return unless first_argument.respond_to?(:binary_operation?) && first_argument.binary_operation?
|
26
|
+
|
27
|
+
operator = first_argument.to_a[1]
|
28
|
+
return if ALLOWED_OPERATORS.include?(operator)
|
29
|
+
|
30
|
+
new_arguments = build_new_arguments(node)
|
31
|
+
|
32
|
+
add_offense(node, message: format(MSG, new_arguments: new_arguments)) do |corrector|
|
33
|
+
corrector.replace(node.loc.selector, 'refute_operator')
|
34
|
+
|
35
|
+
corrector.replace(range_of_arguments(node), new_arguments)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def build_new_arguments(node)
|
42
|
+
lhs, op, rhs = *node.first_argument
|
43
|
+
new_arguments = "#{lhs.source}, :#{op}, #{rhs.source}"
|
44
|
+
|
45
|
+
if node.arguments.count == 2
|
46
|
+
new_arguments << ", #{node.last_argument.source}"
|
47
|
+
else
|
48
|
+
new_arguments
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def range_of_arguments(node)
|
53
|
+
node.first_argument.source_range.begin.join(node.last_argument.source_range.end)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -30,20 +30,25 @@ module RuboCop
|
|
30
30
|
def on_send(node)
|
31
31
|
refute_file_exists(node) do |path, failure_message|
|
32
32
|
failure_message = failure_message.first
|
33
|
-
good_method = build_good_method(path, failure_message)
|
33
|
+
good_method = build_good_method(node, path, failure_message)
|
34
34
|
message = format(MSG, good_method: good_method)
|
35
35
|
|
36
36
|
add_offense(node, message: message) do |corrector|
|
37
|
-
corrector.replace(node,
|
37
|
+
corrector.replace(node.loc.selector, 'refute_path_exists')
|
38
|
+
corrector.replace(node.first_argument, path.source)
|
38
39
|
end
|
39
40
|
end
|
40
41
|
end
|
41
42
|
|
42
43
|
private
|
43
44
|
|
44
|
-
def build_good_method(path, message)
|
45
|
+
def build_good_method(node, path, message)
|
45
46
|
args = [path.source, message&.source].compact.join(', ')
|
46
|
-
|
47
|
+
if node.parenthesized?
|
48
|
+
"refute_path_exists(#{args})"
|
49
|
+
else
|
50
|
+
"refute_path_exists #{args}"
|
51
|
+
end
|
47
52
|
end
|
48
53
|
end
|
49
54
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Minitest
|
6
|
+
# Enforces the use of `refute_same(expected, object)`
|
7
|
+
# over `refute(expected.equal?(actual))`.
|
8
|
+
#
|
9
|
+
# NOTE: Use `refute_same` only when there is a need to compare by identity.
|
10
|
+
# Otherwise, use `refute_equal`.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# # bad
|
14
|
+
# refute(expected.equal?(actual))
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# refute_same(expected, actual)
|
18
|
+
#
|
19
|
+
class RefuteSame < Base
|
20
|
+
extend MinitestCopRule
|
21
|
+
|
22
|
+
define_rule :refute, target_method: :equal?, preferred_method: :refute_same
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Minitest
|
6
|
+
# Enforces the use of `skip` instead of `return` in test methods.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# def test_something
|
11
|
+
# return if condition?
|
12
|
+
# assert_equal(42, something)
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# def test_something
|
17
|
+
# skip if condition?
|
18
|
+
# assert_equal(42, something)
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
class ReturnInTestMethod < Base
|
22
|
+
include MinitestExplorationHelpers
|
23
|
+
extend AutoCorrector
|
24
|
+
|
25
|
+
MSG = 'Use `skip` instead of `return`.'
|
26
|
+
|
27
|
+
def on_return(node)
|
28
|
+
return unless node.ancestors.any? { |parent| test_case?(parent) }
|
29
|
+
return if inside_block?(node)
|
30
|
+
|
31
|
+
add_offense(node) do |corrector|
|
32
|
+
corrector.replace(node, 'skip')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def inside_block?(node)
|
39
|
+
node.ancestors.any?(&:block_type?) || node.ancestors.any?(&:numblock_type?)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Minitest
|
6
|
+
# Checks for skipped tests missing the skipping reason.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# skip
|
11
|
+
# skip('')
|
12
|
+
#
|
13
|
+
# # bad
|
14
|
+
# if condition?
|
15
|
+
# skip
|
16
|
+
# else
|
17
|
+
# skip
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# skip("Reason why the test was skipped")
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# skip if condition?
|
25
|
+
#
|
26
|
+
class SkipWithoutReason < Base
|
27
|
+
MSG = 'Add a reason explaining why the test is skipped.'
|
28
|
+
|
29
|
+
RESTRICT_ON_SEND = %i[skip].freeze
|
30
|
+
|
31
|
+
def on_send(node)
|
32
|
+
return if node.receiver || !blank_argument?(node)
|
33
|
+
|
34
|
+
conditional_node = conditional_parent(node)
|
35
|
+
return if conditional_node && !only_skip_branches?(conditional_node)
|
36
|
+
|
37
|
+
return if node.parent&.resbody_type?
|
38
|
+
|
39
|
+
add_offense(node)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def blank_argument?(node)
|
45
|
+
message = node.first_argument
|
46
|
+
message.nil? || (message.str_type? && message.value == '')
|
47
|
+
end
|
48
|
+
|
49
|
+
def conditional_parent(node)
|
50
|
+
return unless (parent = node.parent)
|
51
|
+
|
52
|
+
if parent.if_type? || parent.case_type?
|
53
|
+
parent
|
54
|
+
elsif parent.when_type?
|
55
|
+
parent.parent
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def only_skip_branches?(node)
|
60
|
+
branches = node.branches.compact
|
61
|
+
branches.size > 1 && branches.all? { |branch| branch.send_type? && branch.method?(:skip) }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Minitest
|
6
|
+
# Checks if test file names start with `test_` or end with `_test.rb`.
|
7
|
+
# Files which define classes having names ending with `Test` are checked.
|
8
|
+
# Not following this convention may result in tests not being run.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# # bad
|
12
|
+
# my_class.rb
|
13
|
+
#
|
14
|
+
# # good
|
15
|
+
# my_class_test.rb
|
16
|
+
# test_my_class.rb
|
17
|
+
#
|
18
|
+
class TestFileName < Base
|
19
|
+
include MinitestExplorationHelpers
|
20
|
+
|
21
|
+
MSG = 'Test file path should start with `test_` or end with `_test.rb`.'
|
22
|
+
|
23
|
+
def on_new_investigation
|
24
|
+
return unless (ast = processed_source.ast)
|
25
|
+
return unless test_file?(ast)
|
26
|
+
|
27
|
+
add_global_offense(MSG) unless valid_file_name?
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def test_file?(node)
|
33
|
+
return true if node.class_type? && test_class?(node)
|
34
|
+
|
35
|
+
node.each_descendant(:class).any? { |class_node| test_class?(class_node) }
|
36
|
+
end
|
37
|
+
|
38
|
+
def valid_file_name?
|
39
|
+
basename = File.basename(processed_source.file_path)
|
40
|
+
|
41
|
+
basename.start_with?('test_') || basename.end_with?('_test.rb')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -37,7 +37,7 @@ module RuboCop
|
|
37
37
|
def on_class(class_node)
|
38
38
|
return unless test_class?(class_node)
|
39
39
|
|
40
|
-
|
40
|
+
class_def_nodes(class_node).each do |node|
|
41
41
|
next unless offense?(node)
|
42
42
|
|
43
43
|
test_method_name = node.loc.name
|
@@ -50,17 +50,6 @@ module RuboCop
|
|
50
50
|
|
51
51
|
private
|
52
52
|
|
53
|
-
def class_elements(class_node)
|
54
|
-
class_def = class_node.body
|
55
|
-
return [] unless class_def
|
56
|
-
|
57
|
-
if class_def.def_type?
|
58
|
-
[class_def]
|
59
|
-
else
|
60
|
-
class_def.each_child_node(:def).to_a
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
53
|
def offense?(node)
|
65
54
|
return false if assertions(node).none?
|
66
55
|
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Minitest
|
6
|
+
# Detects useless assertions (assertions that either always pass or always fail).
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# assert true
|
11
|
+
# assert_equal @foo, @foo
|
12
|
+
# assert_nil [foo, bar]
|
13
|
+
#
|
14
|
+
# # good
|
15
|
+
# assert something
|
16
|
+
# assert_equal foo, bar
|
17
|
+
# assert_nil foo
|
18
|
+
# assert false, "My message"
|
19
|
+
#
|
20
|
+
class UselessAssertion < Base
|
21
|
+
MSG = 'Useless assertion detected.'
|
22
|
+
|
23
|
+
SINGLE_ASSERTION_ARGUMENT_METHODS = %i[
|
24
|
+
assert refute assert_nil refute_nil assert_not assert_empty refute_empty
|
25
|
+
].freeze
|
26
|
+
TWO_ASSERTION_ARGUMENTS_METHODS = %i[
|
27
|
+
assert_equal refute_equal assert_in_delta refute_in_delta
|
28
|
+
assert_in_epsilon refute_in_epsilon assert_same refute_same
|
29
|
+
].freeze
|
30
|
+
|
31
|
+
RESTRICT_ON_SEND = SINGLE_ASSERTION_ARGUMENT_METHODS +
|
32
|
+
TWO_ASSERTION_ARGUMENTS_METHODS +
|
33
|
+
%i[assert_includes refute_includes assert_silent]
|
34
|
+
|
35
|
+
def on_send(node)
|
36
|
+
return if node.receiver
|
37
|
+
|
38
|
+
add_offense(node) if offense?(node)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# rubocop:disable Metrics
|
44
|
+
def offense?(node)
|
45
|
+
expected, actual, = node.arguments
|
46
|
+
|
47
|
+
case node.method_name
|
48
|
+
when *SINGLE_ASSERTION_ARGUMENT_METHODS
|
49
|
+
actual.nil? && expected&.literal? && !expected.xstr_type?
|
50
|
+
when *TWO_ASSERTION_ARGUMENTS_METHODS
|
51
|
+
return false unless expected || actual
|
52
|
+
return false if expected.source != actual.source
|
53
|
+
|
54
|
+
(expected.variable? && actual.variable?) ||
|
55
|
+
(empty_composite?(expected) && empty_composite?(actual))
|
56
|
+
when :assert_includes, :refute_includes
|
57
|
+
expected && empty_composite?(expected)
|
58
|
+
when :assert_silent
|
59
|
+
block_node = node.parent
|
60
|
+
block_node&.body.nil?
|
61
|
+
else
|
62
|
+
false
|
63
|
+
end
|
64
|
+
end
|
65
|
+
# rubocop:enable Metrics
|
66
|
+
|
67
|
+
def empty_composite?(node)
|
68
|
+
return true if node.str_type? && node.value.empty?
|
69
|
+
|
70
|
+
(node.array_type? || node.hash_type?) && node.children.empty?
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|