rspec-expectations 2.14.0 → 3.13.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 (155) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/.document +1 -1
  4. data/.yardopts +1 -1
  5. data/Changelog.md +976 -25
  6. data/{License.txt → LICENSE.md} +5 -3
  7. data/README.md +162 -26
  8. data/lib/rspec/expectations/block_snippet_extractor.rb +253 -0
  9. data/lib/rspec/expectations/configuration.rb +230 -0
  10. data/lib/rspec/expectations/expectation_target.rb +127 -51
  11. data/lib/rspec/expectations/fail_with.rb +17 -57
  12. data/lib/rspec/expectations/failure_aggregator.rb +229 -0
  13. data/lib/rspec/expectations/handler.rb +146 -32
  14. data/lib/rspec/expectations/minitest_integration.rb +58 -0
  15. data/lib/rspec/expectations/syntax.rb +68 -100
  16. data/lib/rspec/expectations/version.rb +1 -1
  17. data/lib/rspec/expectations.rb +58 -23
  18. data/lib/rspec/matchers/aliased_matcher.rb +116 -0
  19. data/lib/rspec/matchers/built_in/all.rb +86 -0
  20. data/lib/rspec/matchers/built_in/base_matcher.rb +191 -20
  21. data/lib/rspec/matchers/built_in/be.rb +114 -114
  22. data/lib/rspec/matchers/built_in/be_between.rb +77 -0
  23. data/lib/rspec/matchers/built_in/be_instance_of.rb +15 -4
  24. data/lib/rspec/matchers/built_in/be_kind_of.rb +10 -1
  25. data/lib/rspec/matchers/built_in/be_within.rb +35 -18
  26. data/lib/rspec/matchers/built_in/change.rb +389 -80
  27. data/lib/rspec/matchers/built_in/compound.rb +290 -0
  28. data/lib/rspec/matchers/built_in/contain_exactly.rb +310 -0
  29. data/lib/rspec/matchers/built_in/count_expectation.rb +169 -0
  30. data/lib/rspec/matchers/built_in/cover.rb +3 -0
  31. data/lib/rspec/matchers/built_in/eq.rb +30 -8
  32. data/lib/rspec/matchers/built_in/eql.rb +23 -8
  33. data/lib/rspec/matchers/built_in/equal.rb +55 -22
  34. data/lib/rspec/matchers/built_in/exist.rb +74 -10
  35. data/lib/rspec/matchers/built_in/has.rb +141 -22
  36. data/lib/rspec/matchers/built_in/have_attributes.rb +114 -0
  37. data/lib/rspec/matchers/built_in/include.rb +184 -32
  38. data/lib/rspec/matchers/built_in/match.rb +95 -1
  39. data/lib/rspec/matchers/built_in/operators.rb +128 -0
  40. data/lib/rspec/matchers/built_in/output.rb +207 -0
  41. data/lib/rspec/matchers/built_in/raise_error.rb +192 -44
  42. data/lib/rspec/matchers/built_in/respond_to.rb +154 -28
  43. data/lib/rspec/matchers/built_in/satisfy.rb +39 -9
  44. data/lib/rspec/matchers/built_in/start_or_end_with.rb +94 -0
  45. data/lib/rspec/matchers/built_in/throw_symbol.rb +58 -14
  46. data/lib/rspec/matchers/built_in/yield.rb +240 -161
  47. data/lib/rspec/matchers/built_in.rb +47 -33
  48. data/lib/rspec/matchers/composable.rb +171 -0
  49. data/lib/rspec/matchers/dsl.rb +531 -10
  50. data/lib/rspec/matchers/english_phrasing.rb +58 -0
  51. data/lib/rspec/matchers/fail_matchers.rb +42 -0
  52. data/lib/rspec/matchers/generated_descriptions.rb +14 -8
  53. data/lib/rspec/matchers/matcher_delegator.rb +61 -0
  54. data/lib/rspec/matchers/matcher_protocol.rb +105 -0
  55. data/lib/rspec/matchers/multi_matcher_diff.rb +82 -0
  56. data/lib/rspec/matchers.rb +520 -173
  57. data.tar.gz.sig +0 -0
  58. metadata +141 -242
  59. metadata.gz.sig +2 -0
  60. data/features/README.md +0 -48
  61. data/features/Upgrade.md +0 -53
  62. data/features/built_in_matchers/README.md +0 -90
  63. data/features/built_in_matchers/be.feature +0 -175
  64. data/features/built_in_matchers/be_within.feature +0 -48
  65. data/features/built_in_matchers/cover.feature +0 -47
  66. data/features/built_in_matchers/end_with.feature +0 -48
  67. data/features/built_in_matchers/equality.feature +0 -139
  68. data/features/built_in_matchers/exist.feature +0 -45
  69. data/features/built_in_matchers/expect_change.feature +0 -59
  70. data/features/built_in_matchers/expect_error.feature +0 -144
  71. data/features/built_in_matchers/have.feature +0 -109
  72. data/features/built_in_matchers/include.feature +0 -174
  73. data/features/built_in_matchers/match.feature +0 -52
  74. data/features/built_in_matchers/operators.feature +0 -227
  75. data/features/built_in_matchers/predicates.feature +0 -137
  76. data/features/built_in_matchers/respond_to.feature +0 -84
  77. data/features/built_in_matchers/satisfy.feature +0 -33
  78. data/features/built_in_matchers/start_with.feature +0 -48
  79. data/features/built_in_matchers/throw_symbol.feature +0 -91
  80. data/features/built_in_matchers/types.feature +0 -116
  81. data/features/built_in_matchers/yield.feature +0 -161
  82. data/features/custom_matchers/access_running_example.feature +0 -53
  83. data/features/custom_matchers/define_diffable_matcher.feature +0 -27
  84. data/features/custom_matchers/define_matcher.feature +0 -368
  85. data/features/custom_matchers/define_matcher_outside_rspec.feature +0 -38
  86. data/features/custom_matchers/define_matcher_with_fluent_interface.feature +0 -24
  87. data/features/customized_message.feature +0 -22
  88. data/features/diffing.feature +0 -85
  89. data/features/implicit_docstrings.feature +0 -52
  90. data/features/step_definitions/additional_cli_steps.rb +0 -22
  91. data/features/support/env.rb +0 -14
  92. data/features/syntax_configuration.feature +0 -71
  93. data/features/test_frameworks/test_unit.feature +0 -44
  94. data/lib/rspec/expectations/deprecation.rb +0 -17
  95. data/lib/rspec/expectations/differ.rb +0 -133
  96. data/lib/rspec/expectations/errors.rb +0 -9
  97. data/lib/rspec/expectations/extensions/array.rb +0 -9
  98. data/lib/rspec/expectations/extensions/object.rb +0 -29
  99. data/lib/rspec/expectations/extensions.rb +0 -2
  100. data/lib/rspec/matchers/be_close.rb +0 -9
  101. data/lib/rspec/matchers/built_in/have.rb +0 -124
  102. data/lib/rspec/matchers/built_in/match_array.rb +0 -51
  103. data/lib/rspec/matchers/built_in/start_and_end_with.rb +0 -48
  104. data/lib/rspec/matchers/compatibility.rb +0 -14
  105. data/lib/rspec/matchers/configuration.rb +0 -108
  106. data/lib/rspec/matchers/extensions/instance_eval_with_args.rb +0 -39
  107. data/lib/rspec/matchers/matcher.rb +0 -300
  108. data/lib/rspec/matchers/method_missing.rb +0 -12
  109. data/lib/rspec/matchers/operator_matcher.rb +0 -109
  110. data/lib/rspec/matchers/pretty.rb +0 -70
  111. data/lib/rspec/matchers/test_unit_integration.rb +0 -11
  112. data/lib/rspec-expectations.rb +0 -1
  113. data/spec/rspec/expectations/differ_spec.rb +0 -192
  114. data/spec/rspec/expectations/expectation_target_spec.rb +0 -82
  115. data/spec/rspec/expectations/extensions/kernel_spec.rb +0 -67
  116. data/spec/rspec/expectations/fail_with_spec.rb +0 -114
  117. data/spec/rspec/expectations/handler_spec.rb +0 -227
  118. data/spec/rspec/expectations/syntax_spec.rb +0 -139
  119. data/spec/rspec/matchers/base_matcher_spec.rb +0 -62
  120. data/spec/rspec/matchers/be_close_spec.rb +0 -22
  121. data/spec/rspec/matchers/be_instance_of_spec.rb +0 -63
  122. data/spec/rspec/matchers/be_kind_of_spec.rb +0 -41
  123. data/spec/rspec/matchers/be_spec.rb +0 -516
  124. data/spec/rspec/matchers/be_within_spec.rb +0 -137
  125. data/spec/rspec/matchers/change_spec.rb +0 -553
  126. data/spec/rspec/matchers/configuration_spec.rb +0 -206
  127. data/spec/rspec/matchers/cover_spec.rb +0 -69
  128. data/spec/rspec/matchers/description_generation_spec.rb +0 -190
  129. data/spec/rspec/matchers/dsl_spec.rb +0 -57
  130. data/spec/rspec/matchers/eq_spec.rb +0 -60
  131. data/spec/rspec/matchers/eql_spec.rb +0 -41
  132. data/spec/rspec/matchers/equal_spec.rb +0 -78
  133. data/spec/rspec/matchers/exist_spec.rb +0 -124
  134. data/spec/rspec/matchers/has_spec.rb +0 -122
  135. data/spec/rspec/matchers/have_spec.rb +0 -455
  136. data/spec/rspec/matchers/include_matcher_integration_spec.rb +0 -30
  137. data/spec/rspec/matchers/include_spec.rb +0 -531
  138. data/spec/rspec/matchers/match_array_spec.rb +0 -194
  139. data/spec/rspec/matchers/match_spec.rb +0 -61
  140. data/spec/rspec/matchers/matcher_spec.rb +0 -471
  141. data/spec/rspec/matchers/matchers_spec.rb +0 -37
  142. data/spec/rspec/matchers/method_missing_spec.rb +0 -28
  143. data/spec/rspec/matchers/operator_matcher_spec.rb +0 -223
  144. data/spec/rspec/matchers/raise_error_spec.rb +0 -485
  145. data/spec/rspec/matchers/respond_to_spec.rb +0 -292
  146. data/spec/rspec/matchers/satisfy_spec.rb +0 -44
  147. data/spec/rspec/matchers/start_with_end_with_spec.rb +0 -186
  148. data/spec/rspec/matchers/throw_symbol_spec.rb +0 -116
  149. data/spec/rspec/matchers/yield_spec.rb +0 -514
  150. data/spec/spec_helper.rb +0 -54
  151. data/spec/support/classes.rb +0 -56
  152. data/spec/support/in_sub_process.rb +0 -38
  153. data/spec/support/matchers.rb +0 -22
  154. data/spec/support/ruby_version.rb +0 -10
  155. data/spec/support/shared_examples.rb +0 -13
@@ -0,0 +1,207 @@
1
+ require 'stringio'
2
+
3
+ module RSpec
4
+ module Matchers
5
+ module BuiltIn
6
+ # @api private
7
+ # Provides the implementation for `output`.
8
+ # Not intended to be instantiated directly.
9
+ class Output < BaseMatcher
10
+ def initialize(expected)
11
+ @expected = expected
12
+ @actual = ""
13
+ @block = nil
14
+ @stream_capturer = NullCapture
15
+ end
16
+
17
+ def matches?(block)
18
+ @block = block
19
+ return false unless Proc === block
20
+ @actual = @stream_capturer.capture(block)
21
+ @expected ? values_match?(@expected, @actual) : captured?
22
+ end
23
+
24
+ def does_not_match?(block)
25
+ !matches?(block) && Proc === block
26
+ end
27
+
28
+ # @api public
29
+ # Tells the matcher to match against stdout.
30
+ # Works only when the main Ruby process prints to stdout
31
+ def to_stdout
32
+ @stream_capturer = CaptureStdout
33
+ self
34
+ end
35
+
36
+ # @api public
37
+ # Tells the matcher to match against stderr.
38
+ # Works only when the main Ruby process prints to stderr
39
+ def to_stderr
40
+ @stream_capturer = CaptureStderr
41
+ self
42
+ end
43
+
44
+ # @api public
45
+ # Tells the matcher to match against stdout.
46
+ # Works when subprocesses print to stdout as well.
47
+ # This is significantly (~30x) slower than `to_stdout`
48
+ def to_stdout_from_any_process
49
+ @stream_capturer = CaptureStreamToTempfile.new("stdout", $stdout)
50
+ self
51
+ end
52
+
53
+ # @api public
54
+ # Tells the matcher to match against stderr.
55
+ # Works when subprocesses print to stderr as well.
56
+ # This is significantly (~30x) slower than `to_stderr`
57
+ def to_stderr_from_any_process
58
+ @stream_capturer = CaptureStreamToTempfile.new("stderr", $stderr)
59
+ self
60
+ end
61
+
62
+ # @api private
63
+ # @return [String]
64
+ def failure_message
65
+ "expected block to #{description}, but #{positive_failure_reason}"
66
+ end
67
+
68
+ # @api private
69
+ # @return [String]
70
+ def failure_message_when_negated
71
+ "expected block to not #{description}, but #{negative_failure_reason}"
72
+ end
73
+
74
+ # @api private
75
+ # @return [String]
76
+ def description
77
+ if @expected
78
+ "output #{description_of @expected} to #{@stream_capturer.name}"
79
+ else
80
+ "output to #{@stream_capturer.name}"
81
+ end
82
+ end
83
+
84
+ # @api private
85
+ # @return [Boolean]
86
+ def diffable?
87
+ true
88
+ end
89
+
90
+ # @api private
91
+ # Indicates this matcher matches against a block.
92
+ # @return [True]
93
+ def supports_block_expectations?
94
+ true
95
+ end
96
+
97
+ # @api private
98
+ # Indicates this matcher matches against a block only.
99
+ # @return [False]
100
+ def supports_value_expectations?
101
+ false
102
+ end
103
+
104
+ private
105
+
106
+ def captured?
107
+ @actual.length > 0
108
+ end
109
+
110
+ def positive_failure_reason
111
+ return "was not a block" unless Proc === @block
112
+ return "output #{actual_output_description}" if @expected
113
+ "did not"
114
+ end
115
+
116
+ def negative_failure_reason
117
+ return "was not a block" unless Proc === @block
118
+ "output #{actual_output_description}"
119
+ end
120
+
121
+ def actual_output_description
122
+ return "nothing" unless captured?
123
+ actual_formatted
124
+ end
125
+ end
126
+
127
+ # @private
128
+ module NullCapture
129
+ def self.name
130
+ "some stream"
131
+ end
132
+
133
+ def self.capture(_block)
134
+ raise "You must chain `to_stdout` or `to_stderr` off of the `output(...)` matcher."
135
+ end
136
+ end
137
+
138
+ # @private
139
+ module CaptureStdout
140
+ def self.name
141
+ 'stdout'
142
+ end
143
+
144
+ def self.capture(block)
145
+ captured_stream = StringIO.new
146
+
147
+ original_stream = $stdout
148
+ $stdout = captured_stream
149
+
150
+ block.call
151
+
152
+ captured_stream.string
153
+ ensure
154
+ $stdout = original_stream
155
+ end
156
+ end
157
+
158
+ # @private
159
+ module CaptureStderr
160
+ def self.name
161
+ 'stderr'
162
+ end
163
+
164
+ def self.capture(block)
165
+ captured_stream = StringIO.new
166
+
167
+ original_stream = $stderr
168
+ $stderr = captured_stream
169
+
170
+ block.call
171
+
172
+ captured_stream.string
173
+ ensure
174
+ $stderr = original_stream
175
+ end
176
+ end
177
+
178
+ # @private
179
+ class CaptureStreamToTempfile < Struct.new(:name, :stream)
180
+ def capture(block)
181
+ # We delay loading tempfile until it is actually needed because
182
+ # we want to minimize stdlibs loaded so that users who use a
183
+ # portion of the stdlib can't have passing specs while forgetting
184
+ # to load it themselves. `CaptureStreamToTempfile` is rarely used
185
+ # and `tempfile` pulls in a bunch of things (delegate, tmpdir,
186
+ # thread, fileutils, etc), so it's worth delaying it until this point.
187
+ require 'tempfile'
188
+
189
+ original_stream = stream.clone
190
+ captured_stream = Tempfile.new(name)
191
+
192
+ begin
193
+ captured_stream.sync = true
194
+ stream.reopen(captured_stream)
195
+ block.call
196
+ captured_stream.rewind
197
+ captured_stream.read
198
+ ensure
199
+ stream.reopen(original_stream)
200
+ captured_stream.close
201
+ captured_stream.unlink
202
+ end
203
+ end
204
+ end
205
+ end
206
+ end
207
+ end
@@ -1,56 +1,141 @@
1
1
  module RSpec
2
2
  module Matchers
3
3
  module BuiltIn
4
+ # @api private
5
+ # Provides the implementation for `raise_error`.
6
+ # Not intended to be instantiated directly.
7
+ # rubocop:disable Metrics/ClassLength
8
+ # rubocop:disable Lint/RescueException
4
9
  class RaiseError
5
- def initialize(expected_error_or_message=Exception, expected_message=nil, &block)
10
+ include Composable
11
+
12
+ # Used as a sentinel value to be able to tell when the user did not pass an
13
+ # argument. We can't use `nil` for that because we need to warn when `nil` is
14
+ # passed in a different way. It's an Object, not a Module, since Module's `===`
15
+ # does not evaluate to true when compared to itself.
16
+ UndefinedValue = Object.new.freeze
17
+
18
+ def initialize(expected_error_or_message, expected_message, &block)
6
19
  @block = block
7
20
  @actual_error = nil
21
+ @warn_about_bare_error = UndefinedValue === expected_error_or_message
22
+ @warn_about_nil_error = expected_error_or_message.nil?
23
+
8
24
  case expected_error_or_message
9
- when String, Regexp
10
- @expected_error, @expected_message = Exception, expected_error_or_message
25
+ when nil, UndefinedValue
26
+ @expected_error = Exception
27
+ @expected_message = expected_message
28
+ when String
29
+ @expected_error = Exception
30
+ @expected_message = expected_error_or_message
11
31
  else
12
- @expected_error, @expected_message = expected_error_or_message, expected_message
32
+ @expected_error = expected_error_or_message
33
+ @expected_message = expected_message
13
34
  end
14
35
  end
15
36
 
16
- def matches?(given_proc, negative_expectation = false)
17
- if negative_expectation && (expecting_specific_exception? || @expected_message)
18
- what_to_deprecate = if expecting_specific_exception? && @expected_message
19
- "`expect { }.not_to raise_error(SpecificErrorClass, message)`"
20
- elsif expecting_specific_exception?
21
- "`expect { }.not_to raise_error(SpecificErrorClass)`"
22
- elsif @expected_message
23
- "`expect { }.not_to raise_error(message)`"
24
- end
25
- RSpec.deprecate(what_to_deprecate, :replacement => "`expect { }.not_to raise_error()`")
26
- end
37
+ # @api public
38
+ # Specifies the expected error message.
39
+ def with_message(expected_message)
40
+ raise_message_already_set if @expected_message
41
+ @warn_about_bare_error = false
42
+ @expected_message = expected_message
43
+ self
44
+ end
45
+
46
+ # rubocop:disable Metrics/MethodLength
47
+ # @private
48
+ def matches?(given_proc, negative_expectation=false, &block)
49
+ @given_proc = given_proc
50
+ @block ||= block
27
51
  @raised_expected_error = false
28
52
  @with_expected_message = false
29
53
  @eval_block = false
30
54
  @eval_block_passed = false
31
- unless given_proc.respond_to?(:call)
32
- ::Kernel.warn "`raise_error` was called with non-proc object #{given_proc.inspect}"
33
- return false
34
- end
55
+
56
+ return false unless Proc === given_proc
57
+
35
58
  begin
36
59
  given_proc.call
37
60
  rescue Exception => @actual_error
38
- if @actual_error == @expected_error || @expected_error === @actual_error
61
+ if values_match?(@expected_error, @actual_error) ||
62
+ values_match?(@expected_error, actual_error_message)
39
63
  @raised_expected_error = true
40
64
  @with_expected_message = verify_message
41
65
  end
42
66
  end
43
67
 
44
68
  unless negative_expectation
45
- eval_block if @raised_expected_error && @with_expected_message && @block
69
+ warn_about_bare_error! if warn_about_bare_error?
70
+ warn_about_nil_error! if warn_about_nil_error?
71
+ eval_block if ready_to_eval_block?
46
72
  end
47
- ensure
48
- return (@raised_expected_error & @with_expected_message) ? (@eval_block ? @eval_block_passed : true) : false
73
+
74
+ expectation_matched?
49
75
  end
50
- alias == matches?
76
+ # rubocop:enable Metrics/MethodLength
51
77
 
78
+ # @private
52
79
  def does_not_match?(given_proc)
53
- !matches?(given_proc, :negative_expectation)
80
+ warn_for_negative_false_positives!
81
+ !matches?(given_proc, :negative_expectation) && Proc === given_proc
82
+ end
83
+
84
+ # @private
85
+ def supports_block_expectations?
86
+ true
87
+ end
88
+
89
+ # @private
90
+ def supports_value_expectations?
91
+ false
92
+ end
93
+
94
+ # @private
95
+ def expects_call_stack_jump?
96
+ true
97
+ end
98
+
99
+ # @api private
100
+ # @return [String]
101
+ def failure_message
102
+ @eval_block ? actual_error_message : "expected #{expected_error}#{given_error}"
103
+ end
104
+
105
+ # @api private
106
+ # @return [String]
107
+ def failure_message_when_negated
108
+ "expected no #{expected_error}#{given_error}"
109
+ end
110
+
111
+ # @api private
112
+ # @return [String]
113
+ def description
114
+ "raise #{expected_error}"
115
+ end
116
+
117
+ private
118
+
119
+ def actual_error_message
120
+ return nil unless @actual_error
121
+
122
+ @actual_error.respond_to?(:original_message) ? @actual_error.original_message : @actual_error.message
123
+ end
124
+
125
+ def expectation_matched?
126
+ error_and_message_match? && block_matches?
127
+ end
128
+
129
+ def error_and_message_match?
130
+ @raised_expected_error && @with_expected_message
131
+ end
132
+
133
+ def block_matches?
134
+ @eval_block ? @eval_block_passed : true
135
+ end
136
+
137
+ def ready_to_eval_block?
138
+ @raised_expected_error && @with_expected_message && @block
54
139
  end
55
140
 
56
141
  def eval_block
@@ -64,38 +149,88 @@ module RSpec
64
149
  end
65
150
 
66
151
  def verify_message
67
- case @expected_message
68
- when nil
69
- true
70
- when Regexp
71
- @expected_message =~ @actual_error.message
72
- else
73
- @expected_message == @actual_error.message
74
- end
152
+ return true if @expected_message.nil?
153
+ values_match?(@expected_message, actual_error_message.to_s)
75
154
  end
76
155
 
77
- def failure_message_for_should
78
- @eval_block ? @actual_error.message : "expected #{expected_error}#{given_error}"
156
+ def warn_for_negative_false_positives!
157
+ expression = if expecting_specific_exception? && @expected_message
158
+ "`expect { }.not_to raise_error(SpecificErrorClass, message)`"
159
+ elsif expecting_specific_exception?
160
+ "`expect { }.not_to raise_error(SpecificErrorClass)`"
161
+ elsif @expected_message
162
+ "`expect { }.not_to raise_error(message)`"
163
+ elsif @warn_about_nil_error
164
+ "`expect { }.not_to raise_error(nil)`"
165
+ end
166
+
167
+ return unless expression
168
+
169
+ warn_about_negative_false_positive! expression
79
170
  end
80
171
 
81
- def failure_message_for_should_not
82
- "expected no #{expected_error}#{given_error}"
172
+ def handle_warning(message)
173
+ RSpec::Expectations.configuration.false_positives_handler.call(message)
83
174
  end
84
175
 
85
- def description
86
- "raise #{expected_error}"
176
+ def warn_about_bare_error?
177
+ @warn_about_bare_error && @block.nil?
178
+ end
179
+
180
+ def warn_about_nil_error?
181
+ @warn_about_nil_error
182
+ end
183
+
184
+ def warn_about_bare_error!
185
+ handle_warning("Using the `raise_error` matcher without providing a specific " \
186
+ "error or message risks false positives, since `raise_error` " \
187
+ "will match when Ruby raises a `NoMethodError`, `NameError` or " \
188
+ "`ArgumentError`, potentially allowing the expectation to pass " \
189
+ "without even executing the method you are intending to call. " \
190
+ "#{warning}"\
191
+ "Instead consider providing a specific error class or message. " \
192
+ "This message can be suppressed by setting: " \
193
+ "`RSpec::Expectations.configuration.on_potential_false" \
194
+ "_positives = :nothing`")
195
+ end
196
+
197
+ def warn_about_nil_error!
198
+ handle_warning("Using the `raise_error` matcher with a `nil` error is probably " \
199
+ "unintentional, it risks false positives, since `raise_error` " \
200
+ "will match when Ruby raises a `NoMethodError`, `NameError` or " \
201
+ "`ArgumentError`, potentially allowing the expectation to pass " \
202
+ "without even executing the method you are intending to call. " \
203
+ "#{warning}"\
204
+ "Instead consider providing a specific error class or message. " \
205
+ "This message can be suppressed by setting: " \
206
+ "`RSpec::Expectations.configuration.on_potential_false" \
207
+ "_positives = :nothing`")
87
208
  end
88
209
 
89
- private
210
+ def warn_about_negative_false_positive!(expression)
211
+ handle_warning("Using #{expression} risks false positives, since literally " \
212
+ "any other error would cause the expectation to pass, " \
213
+ "including those raised by Ruby (e.g. `NoMethodError`, `NameError` " \
214
+ "and `ArgumentError`), meaning the code you are intending to test " \
215
+ "may not even get reached. Instead consider using " \
216
+ "`expect { }.not_to raise_error` or `expect { }.to raise_error" \
217
+ "(DifferentSpecificErrorClass)`. This message can be suppressed by " \
218
+ "setting: `RSpec::Expectations.configuration.on_potential_false" \
219
+ "_positives = :nothing`")
220
+ end
90
221
 
91
222
  def expected_error
92
223
  case @expected_message
93
224
  when nil
94
- @expected_error.inspect
225
+ if RSpec::Support.is_a_matcher?(@expected_error)
226
+ "Exception with #{description_of(@expected_error)}"
227
+ else
228
+ description_of(@expected_error)
229
+ end
95
230
  when Regexp
96
- "#{@expected_error} with message matching #{@expected_message.inspect}"
231
+ "#{@expected_error} with message matching #{description_of(@expected_message)}"
97
232
  else
98
- "#{@expected_error} with #{@expected_message.inspect}"
233
+ "#{@expected_error} with #{description_of(@expected_message)}"
99
234
  end
100
235
  end
101
236
 
@@ -105,11 +240,12 @@ module RSpec
105
240
  end
106
241
 
107
242
  def given_error
243
+ return " but was not given a block" unless Proc === @given_proc
108
244
  return " but nothing was raised" unless @actual_error
109
245
 
110
246
  backtrace = format_backtrace(@actual_error.backtrace)
111
247
  [
112
- ", got #{@actual_error.inspect} with backtrace:",
248
+ ", got #{description_of(@actual_error)} with backtrace:",
113
249
  *backtrace
114
250
  ].join("\n # ")
115
251
  end
@@ -117,7 +253,19 @@ module RSpec
117
253
  def expecting_specific_exception?
118
254
  @expected_error != Exception
119
255
  end
256
+
257
+ def raise_message_already_set
258
+ raise "`expect { }.to raise_error(message).with_message(message)` is not valid. " \
259
+ 'The matcher only allows the expected message to be specified once'
260
+ end
261
+
262
+ def warning
263
+ warning = "Actual error raised was #{description_of(@actual_error)}. "
264
+ warning if @actual_error
265
+ end
120
266
  end
267
+ # rubocop:enable Lint/RescueException
268
+ # rubocop:enable Metrics/ClassLength
121
269
  end
122
270
  end
123
271
  end