rspec-expectations 2.11.3 → 3.11.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 (152) 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 +1026 -21
  6. data/{License.txt → LICENSE.md} +5 -3
  7. data/README.md +174 -78
  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 +130 -55
  11. data/lib/rspec/expectations/fail_with.rb +17 -33
  12. data/lib/rspec/expectations/failure_aggregator.rb +212 -0
  13. data/lib/rspec/expectations/handler.rb +163 -29
  14. data/lib/rspec/expectations/minitest_integration.rb +58 -0
  15. data/lib/rspec/expectations/syntax.rb +68 -54
  16. data/lib/rspec/expectations/version.rb +1 -1
  17. data/lib/rspec/expectations.rb +59 -24
  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 +150 -20
  21. data/lib/rspec/matchers/built_in/be.rb +115 -109
  22. data/lib/rspec/matchers/built_in/be_between.rb +77 -0
  23. data/lib/rspec/matchers/built_in/be_instance_of.rb +16 -1
  24. data/lib/rspec/matchers/built_in/be_kind_of.rb +10 -1
  25. data/lib/rspec/matchers/built_in/be_within.rb +43 -17
  26. data/lib/rspec/matchers/built_in/change.rb +392 -75
  27. data/lib/rspec/matchers/built_in/compound.rb +290 -0
  28. data/lib/rspec/matchers/built_in/contain_exactly.rb +302 -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 +26 -8
  32. data/lib/rspec/matchers/built_in/eql.rb +19 -8
  33. data/lib/rspec/matchers/built_in/equal.rb +56 -19
  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 +175 -20
  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 +212 -38
  42. data/lib/rspec/matchers/built_in/respond_to.rb +155 -29
  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 +252 -98
  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 +530 -10
  50. data/lib/rspec/matchers/english_phrasing.rb +58 -0
  51. data/lib/rspec/matchers/expecteds_for_multiple_diffs.rb +82 -0
  52. data/lib/rspec/matchers/fail_matchers.rb +42 -0
  53. data/lib/rspec/matchers/generated_descriptions.rb +15 -10
  54. data/lib/rspec/matchers/matcher_delegator.rb +35 -0
  55. data/lib/rspec/matchers/matcher_protocol.rb +105 -0
  56. data/lib/rspec/matchers.rb +604 -252
  57. data.tar.gz.sig +0 -0
  58. metadata +178 -278
  59. metadata.gz.sig +0 -0
  60. data/features/README.md +0 -49
  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 -173
  64. data/features/built_in_matchers/be_within.feature +0 -46
  65. data/features/built_in_matchers/cover.feature +0 -45
  66. data/features/built_in_matchers/end_with.feature +0 -46
  67. data/features/built_in_matchers/equality.feature +0 -145
  68. data/features/built_in_matchers/exist.feature +0 -43
  69. data/features/built_in_matchers/expect_change.feature +0 -59
  70. data/features/built_in_matchers/expect_error.feature +0 -138
  71. data/features/built_in_matchers/have.feature +0 -103
  72. data/features/built_in_matchers/include.feature +0 -121
  73. data/features/built_in_matchers/match.feature +0 -50
  74. data/features/built_in_matchers/operators.feature +0 -221
  75. data/features/built_in_matchers/predicates.feature +0 -128
  76. data/features/built_in_matchers/respond_to.feature +0 -78
  77. data/features/built_in_matchers/satisfy.feature +0 -31
  78. data/features/built_in_matchers/start_with.feature +0 -46
  79. data/features/built_in_matchers/throw_symbol.feature +0 -85
  80. data/features/built_in_matchers/types.feature +0 -114
  81. data/features/built_in_matchers/yield.feature +0 -146
  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 -340
  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 -5
  92. data/features/syntax_configuration.feature +0 -68
  93. data/features/test_frameworks/test_unit.feature +0 -46
  94. data/lib/rspec/expectations/deprecation.rb +0 -38
  95. data/lib/rspec/expectations/differ.rb +0 -81
  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 -39
  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 -108
  102. data/lib/rspec/matchers/built_in/match_array.rb +0 -45
  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 -66
  106. data/lib/rspec/matchers/extensions/instance_eval_with_args.rb +0 -39
  107. data/lib/rspec/matchers/matcher.rb +0 -299
  108. data/lib/rspec/matchers/method_missing.rb +0 -12
  109. data/lib/rspec/matchers/operator_matcher.rb +0 -84
  110. data/lib/rspec/matchers/pretty.rb +0 -60
  111. data/lib/rspec-expectations.rb +0 -1
  112. data/spec/rspec/expectations/differ_spec.rb +0 -153
  113. data/spec/rspec/expectations/expectation_target_spec.rb +0 -65
  114. data/spec/rspec/expectations/extensions/kernel_spec.rb +0 -67
  115. data/spec/rspec/expectations/fail_with_spec.rb +0 -70
  116. data/spec/rspec/expectations/handler_spec.rb +0 -206
  117. data/spec/rspec/matchers/base_matcher_spec.rb +0 -60
  118. data/spec/rspec/matchers/be_close_spec.rb +0 -22
  119. data/spec/rspec/matchers/be_instance_of_spec.rb +0 -40
  120. data/spec/rspec/matchers/be_kind_of_spec.rb +0 -37
  121. data/spec/rspec/matchers/be_spec.rb +0 -452
  122. data/spec/rspec/matchers/be_within_spec.rb +0 -80
  123. data/spec/rspec/matchers/change_spec.rb +0 -528
  124. data/spec/rspec/matchers/configuration_spec.rb +0 -202
  125. data/spec/rspec/matchers/cover_spec.rb +0 -69
  126. data/spec/rspec/matchers/description_generation_spec.rb +0 -176
  127. data/spec/rspec/matchers/dsl_spec.rb +0 -57
  128. data/spec/rspec/matchers/eq_spec.rb +0 -54
  129. data/spec/rspec/matchers/eql_spec.rb +0 -41
  130. data/spec/rspec/matchers/equal_spec.rb +0 -60
  131. data/spec/rspec/matchers/exist_spec.rb +0 -110
  132. data/spec/rspec/matchers/has_spec.rb +0 -118
  133. data/spec/rspec/matchers/have_spec.rb +0 -461
  134. data/spec/rspec/matchers/include_spec.rb +0 -367
  135. data/spec/rspec/matchers/match_array_spec.rb +0 -124
  136. data/spec/rspec/matchers/match_spec.rb +0 -61
  137. data/spec/rspec/matchers/matcher_spec.rb +0 -434
  138. data/spec/rspec/matchers/matchers_spec.rb +0 -31
  139. data/spec/rspec/matchers/method_missing_spec.rb +0 -24
  140. data/spec/rspec/matchers/operator_matcher_spec.rb +0 -221
  141. data/spec/rspec/matchers/raise_error_spec.rb +0 -344
  142. data/spec/rspec/matchers/respond_to_spec.rb +0 -295
  143. data/spec/rspec/matchers/satisfy_spec.rb +0 -44
  144. data/spec/rspec/matchers/start_with_end_with_spec.rb +0 -182
  145. data/spec/rspec/matchers/throw_symbol_spec.rb +0 -116
  146. data/spec/rspec/matchers/yield_spec.rb +0 -402
  147. data/spec/spec_helper.rb +0 -27
  148. data/spec/support/classes.rb +0 -56
  149. data/spec/support/in_sub_process.rb +0 -31
  150. data/spec/support/matchers.rb +0 -22
  151. data/spec/support/ruby_version.rb +0 -10
  152. 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,41 +1,142 @@
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 ClassLength
8
+ # rubocop:disable 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)
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 MethodLength
47
+ # @private
48
+ def matches?(given_proc, negative_expectation=false, &block)
49
+ @given_proc = given_proc
50
+ @block ||= block
17
51
  @raised_expected_error = false
18
52
  @with_expected_message = false
19
53
  @eval_block = false
20
54
  @eval_block_passed = false
55
+
56
+ return false unless Proc === given_proc
57
+
21
58
  begin
22
59
  given_proc.call
23
- rescue @expected_error => @actual_error
24
- @raised_expected_error = true
25
- @with_expected_message = verify_message
26
60
  rescue Exception => @actual_error
27
- # This clause should be empty, but rcov will not report it as covered
28
- # unless something (anything) is executed within the clause
29
- "http://eigenclass.org/hiki.rb?rcov-0.8.0"
61
+ if values_match?(@expected_error, @actual_error) ||
62
+ values_match?(@expected_error, actual_error_message)
63
+ @raised_expected_error = true
64
+ @with_expected_message = verify_message
65
+ end
30
66
  end
31
67
 
32
- unless negative_expectation?
33
- eval_block if @raised_expected_error && @with_expected_message && @block
68
+ unless negative_expectation
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?
34
72
  end
35
- ensure
36
- return (@raised_expected_error & @with_expected_message) ? (@eval_block ? @eval_block_passed : true) : false
73
+
74
+ expectation_matched?
75
+ end
76
+ # rubocop:enable MethodLength
77
+
78
+ # @private
79
+ def does_not_match?(given_proc)
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
37
139
  end
38
- alias == matches?
39
140
 
40
141
  def eval_block
41
142
  @eval_block = true
@@ -48,50 +149,123 @@ module RSpec
48
149
  end
49
150
 
50
151
  def verify_message
51
- case @expected_message
52
- when nil
53
- true
54
- when Regexp
55
- @expected_message =~ @actual_error.message
56
- else
57
- @expected_message == @actual_error.message
58
- end
152
+ return true if @expected_message.nil?
153
+ values_match?(@expected_message, actual_error_message.to_s)
59
154
  end
60
155
 
61
- def failure_message_for_should
62
- @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
63
170
  end
64
171
 
65
- def failure_message_for_should_not
66
- "expected no #{expected_error}#{given_error}"
172
+ def handle_warning(message)
173
+ RSpec::Expectations.configuration.false_positives_handler.call(message)
67
174
  end
68
175
 
69
- def description
70
- "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`")
71
208
  end
72
209
 
73
- 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
74
221
 
75
222
  def expected_error
76
223
  case @expected_message
77
224
  when nil
78
- @expected_error
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
79
230
  when Regexp
80
- "#{@expected_error} with message matching #{@expected_message.inspect}"
231
+ "#{@expected_error} with message matching #{description_of(@expected_message)}"
81
232
  else
82
- "#{@expected_error} with #{@expected_message.inspect}"
233
+ "#{@expected_error} with #{description_of(@expected_message)}"
83
234
  end
84
235
  end
85
236
 
237
+ def format_backtrace(backtrace)
238
+ formatter = Matchers.configuration.backtrace_formatter
239
+ formatter.format_backtrace(backtrace)
240
+ end
241
+
86
242
  def given_error
87
- @actual_error.nil? ? " but nothing was raised" : ", got #{@actual_error.inspect}"
243
+ return " but was not given a block" unless Proc === @given_proc
244
+ return " but nothing was raised" unless @actual_error
245
+
246
+ backtrace = format_backtrace(@actual_error.backtrace)
247
+ [
248
+ ", got #{description_of(@actual_error)} with backtrace:",
249
+ *backtrace
250
+ ].join("\n # ")
251
+ end
252
+
253
+ def expecting_specific_exception?
254
+ @expected_error != Exception
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'
88
260
  end
89
261
 
90
- def negative_expectation?
91
- # YES - I'm a bad person... help me find a better way - ryand
92
- caller.first(3).find { |s| s =~ /should_not/ }
262
+ def warning
263
+ warning = "Actual error raised was #{description_of(@actual_error)}. "
264
+ warning if @actual_error
93
265
  end
94
266
  end
267
+ # rubocop:enable RescueException
268
+ # rubocop:enable ClassLength
95
269
  end
96
270
  end
97
271
  end