rubocop-rspec 1.33.0 → 1.34.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -2
- data/README.md +10 -2
- data/config/default.yml +6 -1
- data/lib/rubocop/cop/rspec/any_instance.rb +0 -1
- data/lib/rubocop/cop/rspec/around_block.rb +1 -2
- data/lib/rubocop/cop/rspec/before_after_all.rb +0 -1
- data/lib/rubocop/cop/rspec/context_wording.rb +18 -17
- data/lib/rubocop/cop/rspec/describe_class.rb +1 -1
- data/lib/rubocop/cop/rspec/describe_method.rb +1 -1
- data/lib/rubocop/cop/rspec/describe_symbol.rb +1 -1
- data/lib/rubocop/cop/rspec/described_class.rb +79 -13
- data/lib/rubocop/cop/rspec/empty_example_group.rb +1 -1
- data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +1 -1
- data/lib/rubocop/cop/rspec/example_length.rb +1 -1
- data/lib/rubocop/cop/rspec/example_wording.rb +6 -4
- data/lib/rubocop/cop/rspec/expect_actual.rb +1 -1
- data/lib/rubocop/cop/rspec/expect_output.rb +2 -0
- data/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +1 -1
- data/lib/rubocop/cop/rspec/factory_bot/create_list.rb +1 -2
- data/lib/rubocop/cop/rspec/file_path.rb +0 -1
- data/lib/rubocop/cop/rspec/focus.rb +1 -1
- data/lib/rubocop/cop/rspec/hook_argument.rb +0 -1
- data/lib/rubocop/cop/rspec/instance_spy.rb +1 -1
- data/lib/rubocop/cop/rspec/instance_variable.rb +1 -1
- data/lib/rubocop/cop/rspec/invalid_predicate_matcher.rb +1 -1
- data/lib/rubocop/cop/rspec/it_behaves_like.rb +1 -1
- data/lib/rubocop/cop/rspec/iterated_expectation.rb +1 -1
- data/lib/rubocop/cop/rspec/leading_subject.rb +0 -1
- data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +128 -0
- data/lib/rubocop/cop/rspec/let_before_examples.rb +1 -1
- data/lib/rubocop/cop/rspec/let_setup.rb +2 -4
- data/lib/rubocop/cop/rspec/missing_example_group_argument.rb +1 -2
- data/lib/rubocop/cop/rspec/multiple_describes.rb +1 -1
- data/lib/rubocop/cop/rspec/multiple_expectations.rb +32 -16
- data/lib/rubocop/cop/rspec/multiple_subjects.rb +1 -1
- data/lib/rubocop/cop/rspec/nested_groups.rb +0 -1
- data/lib/rubocop/cop/rspec/overwriting_setup.rb +0 -1
- data/lib/rubocop/cop/rspec/pending.rb +1 -1
- data/lib/rubocop/cop/rspec/predicate_matcher.rb +0 -3
- data/lib/rubocop/cop/rspec/repeated_description.rb +1 -1
- data/lib/rubocop/cop/rspec/repeated_example.rb +1 -1
- data/lib/rubocop/cop/rspec/scattered_let.rb +1 -1
- data/lib/rubocop/cop/rspec/scattered_setup.rb +1 -1
- data/lib/rubocop/cop/rspec/shared_context.rb +0 -1
- data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +1 -1
- data/lib/rubocop/cop/rspec/subject_stub.rb +17 -20
- data/lib/rubocop/cop/rspec/unspecified_exception.rb +1 -4
- data/lib/rubocop/cop/rspec/verified_doubles.rb +1 -1
- data/lib/rubocop/cop/rspec/void_expect.rb +1 -1
- data/lib/rubocop/cop/rspec_cops.rb +1 -0
- data/lib/rubocop/rspec/language.rb +1 -1
- data/lib/rubocop/rspec/top_level_describe.rb +0 -4
- data/lib/rubocop/rspec/version.rb +1 -1
- data/spec/rubocop/cop/rspec/cop_spec.rb +3 -3
- data/spec/rubocop/cop/rspec/describe_class_spec.rb +7 -0
- data/spec/rubocop/cop/rspec/described_class_spec.rb +113 -80
- data/spec/rubocop/cop/rspec/example_wording_spec.rb +33 -0
- data/spec/rubocop/cop/rspec/leaky_constant_declaration_spec.rb +91 -0
- data/spec/rubocop/cop/rspec/let_setup_spec.rb +2 -2
- data/spec/rubocop/cop/rspec/multiple_expectations_spec.rb +64 -37
- data/spec/rubocop/cop/rspec/subject_stub_spec.rb +113 -14
- data/spec/rubocop/rspec/language/selector_set_spec.rb +2 -2
- metadata +5 -2
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Checks that no class, module, or constant is declared.
|
7
|
+
#
|
8
|
+
# Constants, including classes and modules, when declared in a block
|
9
|
+
# scope, are defined in global namespace, and leak between examples.
|
10
|
+
#
|
11
|
+
# If several examples may define a `DummyClass`, instead of being a
|
12
|
+
# blank slate class as it will be in the first example, subsequent
|
13
|
+
# examples will be reopening it and modifying its behaviour in
|
14
|
+
# unpredictable ways.
|
15
|
+
# Even worse when a class that exists in the codebase is reopened.
|
16
|
+
#
|
17
|
+
# Anonymous classes are fine, since they don't result in global
|
18
|
+
# namespace name clashes.
|
19
|
+
#
|
20
|
+
# @see https://relishapp.com/rspec/rspec-mocks/docs/mutating-constants
|
21
|
+
#
|
22
|
+
# @example Constants leak between examples
|
23
|
+
# # bad
|
24
|
+
# describe SomeClass do
|
25
|
+
# OtherClass = Struct.new
|
26
|
+
# CONSTANT_HERE = 'I leak into global namespace'
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# # good
|
30
|
+
# describe SomeClass do
|
31
|
+
# before do
|
32
|
+
# stub_const('OtherClass', Struct.new)
|
33
|
+
# stub_const('CONSTANT_HERE', 'I only exist during this example')
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# @example
|
38
|
+
# # bad
|
39
|
+
# describe SomeClass do
|
40
|
+
# class FooClass < described_class
|
41
|
+
# def double_that
|
42
|
+
# some_base_method * 2
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# it { expect(FooClass.new.double_that).to eq(4) }
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# # good - anonymous class, no constant needs to be defined
|
50
|
+
# let(:foo_class) do
|
51
|
+
# Class.new(described_class) do
|
52
|
+
# def double_that
|
53
|
+
# some_base_method * 2
|
54
|
+
# end
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# it { expect(foo_class.new.double_that).to eq(4) }
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# # good - constant is stubbed
|
61
|
+
# describe SomeClass do
|
62
|
+
# before do
|
63
|
+
# foo_class = Class.new(described_class) do
|
64
|
+
# def do_something
|
65
|
+
# end
|
66
|
+
# end
|
67
|
+
# stub_const('FooClass', foo_class)
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# it { expect(FooClass.new.double_that).to eq(4) }
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# @example
|
74
|
+
# # bad
|
75
|
+
# describe SomeClass do
|
76
|
+
# module SomeModule
|
77
|
+
# class SomeClass
|
78
|
+
# def do_something
|
79
|
+
# end
|
80
|
+
# end
|
81
|
+
# end
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# # good
|
85
|
+
# describe SomeClass do
|
86
|
+
# before do
|
87
|
+
# foo_class = Class.new(described_class) do
|
88
|
+
# def do_something
|
89
|
+
# end
|
90
|
+
# end
|
91
|
+
# stub_const('SomeModule::SomeClass', foo_class)
|
92
|
+
# end
|
93
|
+
# end
|
94
|
+
class LeakyConstantDeclaration < Cop
|
95
|
+
MSG_CONST = 'Stub constant instead of declaring explicitly.'
|
96
|
+
MSG_CLASS = 'Stub class constant instead of declaring explicitly.'
|
97
|
+
MSG_MODULE = 'Stub module constant instead of declaring explicitly.'
|
98
|
+
|
99
|
+
def on_casgn(node)
|
100
|
+
return unless inside_describe_block?(node)
|
101
|
+
|
102
|
+
add_offense(node, message: MSG_CONST)
|
103
|
+
end
|
104
|
+
|
105
|
+
def on_class(node)
|
106
|
+
return unless inside_describe_block?(node)
|
107
|
+
|
108
|
+
add_offense(node, message: MSG_CLASS)
|
109
|
+
end
|
110
|
+
|
111
|
+
def on_module(node)
|
112
|
+
return unless inside_describe_block?(node)
|
113
|
+
|
114
|
+
add_offense(node, message: MSG_MODULE)
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def inside_describe_block?(node)
|
120
|
+
node.each_ancestor(:block).any?(&method(:in_example_or_shared_group?))
|
121
|
+
end
|
122
|
+
|
123
|
+
def_node_matcher :in_example_or_shared_group?,
|
124
|
+
(ExampleGroups::ALL + SharedGroups::ALL).block_pattern
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -26,9 +26,7 @@ module RuboCop
|
|
26
26
|
# expect(Widget.count).to eq(1)
|
27
27
|
# end
|
28
28
|
class LetSetup < Cop
|
29
|
-
|
30
|
-
|
31
|
-
MSG = 'Do not use `let!` for test setup.'
|
29
|
+
MSG = 'Do not use `let!` to setup objects not referenced in tests.'
|
32
30
|
|
33
31
|
def_node_search :let_bang, <<-PATTERN
|
34
32
|
(block $(send nil? :let! (sym $_)) args ...)
|
@@ -40,7 +38,7 @@ module RuboCop
|
|
40
38
|
return unless example_group?(node)
|
41
39
|
|
42
40
|
unused_let_bang(node) do |let|
|
43
|
-
add_offense(let
|
41
|
+
add_offense(let)
|
44
42
|
end
|
45
43
|
end
|
46
44
|
|
@@ -26,8 +26,7 @@ module RuboCop
|
|
26
26
|
return unless example_group?(node)
|
27
27
|
return if node.send_node.arguments?
|
28
28
|
|
29
|
-
add_offense(node,
|
30
|
-
message: format(MSG, method: node.method_name))
|
29
|
+
add_offense(node, message: format(MSG, method: node.method_name))
|
31
30
|
end
|
32
31
|
end
|
33
32
|
end
|
@@ -50,20 +50,20 @@ module RuboCop
|
|
50
50
|
|
51
51
|
MSG = 'Example has too many expectations [%<total>d/%<max>d].'
|
52
52
|
|
53
|
-
def_node_search :
|
54
|
-
def_node_search :
|
53
|
+
def_node_search :with_aggregate_failures?, '(sym :aggregate_failures)'
|
54
|
+
def_node_search :disabled_aggregate_failures?, <<-PATTERN
|
55
55
|
(pair (sym :aggregate_failures) (false))
|
56
56
|
PATTERN
|
57
57
|
|
58
58
|
def_node_matcher :expect?, Expectations::ALL.send_pattern
|
59
|
-
def_node_matcher :
|
59
|
+
def_node_matcher :aggregate_failures_block?, <<-PATTERN
|
60
60
|
(block (send _ :aggregate_failures ...) ...)
|
61
61
|
PATTERN
|
62
62
|
|
63
63
|
def on_block(node)
|
64
64
|
return unless example?(node)
|
65
65
|
|
66
|
-
return if
|
66
|
+
return if example_with_aggregate_failures?(node)
|
67
67
|
|
68
68
|
expectations_count = to_enum(:find_expectation, node).count
|
69
69
|
|
@@ -76,19 +76,40 @@ module RuboCop
|
|
76
76
|
|
77
77
|
private
|
78
78
|
|
79
|
-
def
|
80
|
-
|
79
|
+
def example_with_aggregate_failures?(example_node)
|
80
|
+
node_with_aggregate_failures = find_aggregate_failures(example_node)
|
81
|
+
return false unless node_with_aggregate_failures
|
81
82
|
|
82
|
-
(
|
83
|
-
|
84
|
-
|
83
|
+
aggregate_failures?(node_with_aggregate_failures)
|
84
|
+
end
|
85
|
+
|
86
|
+
def find_aggregate_failures(example_node)
|
87
|
+
example_node.send_node.each_ancestor(:block)
|
88
|
+
.find { |block_node| aggregate_failures_present?(block_node) }
|
89
|
+
end
|
90
|
+
|
91
|
+
def aggregate_failures_present?(node)
|
92
|
+
metadata(node)&.any?(&method(:with_aggregate_failures?))
|
93
|
+
end
|
94
|
+
|
95
|
+
def aggregate_failures?(example_or_group_node)
|
96
|
+
metadata(example_or_group_node)&.any? do |metadata|
|
97
|
+
with_aggregate_failures?(metadata) &&
|
98
|
+
!disabled_aggregate_failures?(metadata)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def metadata(example_or_group_node)
|
103
|
+
RuboCop::RSpec::Example
|
104
|
+
.new(example_or_group_node)
|
105
|
+
.metadata
|
85
106
|
end
|
86
107
|
|
87
108
|
def find_expectation(node, &block)
|
88
|
-
yield if expect?(node) ||
|
109
|
+
yield if expect?(node) || aggregate_failures_block?(node)
|
89
110
|
|
90
111
|
# do not search inside of aggregate_failures block
|
91
|
-
return if
|
112
|
+
return if aggregate_failures_block?(node)
|
92
113
|
|
93
114
|
node.each_child_node do |child|
|
94
115
|
find_expectation(child, &block)
|
@@ -98,7 +119,6 @@ module RuboCop
|
|
98
119
|
def flag_example(node, expectation_count:)
|
99
120
|
add_offense(
|
100
121
|
node.send_node,
|
101
|
-
location: :expression,
|
102
122
|
message: format(
|
103
123
|
MSG,
|
104
124
|
total: expectation_count,
|
@@ -110,10 +130,6 @@ module RuboCop
|
|
110
130
|
def max_expectations
|
111
131
|
Integer(cop_config.fetch('Max', 1))
|
112
132
|
end
|
113
|
-
|
114
|
-
def aggregated_failures_by_default?
|
115
|
-
cop_config.fetch('AggregateFailuresByDefault', false)
|
116
|
-
end
|
117
133
|
end
|
118
134
|
end
|
119
135
|
end
|
@@ -17,7 +17,6 @@ module RuboCop
|
|
17
17
|
predicate_in_actual?(node) do |predicate|
|
18
18
|
add_offense(
|
19
19
|
node,
|
20
|
-
location: :expression,
|
21
20
|
message: message_inflected(predicate)
|
22
21
|
)
|
23
22
|
end
|
@@ -143,7 +142,6 @@ module RuboCop
|
|
143
142
|
predicate_matcher_block?(node) do |_actual, matcher|
|
144
143
|
add_offense(
|
145
144
|
node,
|
146
|
-
location: :expression,
|
147
145
|
message: message_explicit(matcher)
|
148
146
|
)
|
149
147
|
ignore_node(node.children.first)
|
@@ -155,7 +153,6 @@ module RuboCop
|
|
155
153
|
predicate_matcher?(node) do |_actual, matcher|
|
156
154
|
add_offense(
|
157
155
|
node,
|
158
|
-
location: :expression,
|
159
156
|
message: message_explicit(matcher)
|
160
157
|
)
|
161
158
|
end
|