rspec-expectations 3.0.4 → 3.12.3

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 (59) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.document +1 -1
  4. data/.yardopts +1 -1
  5. data/Changelog.md +530 -5
  6. data/{License.txt → LICENSE.md} +5 -4
  7. data/README.md +73 -31
  8. data/lib/rspec/expectations/block_snippet_extractor.rb +253 -0
  9. data/lib/rspec/expectations/configuration.rb +96 -1
  10. data/lib/rspec/expectations/expectation_target.rb +82 -38
  11. data/lib/rspec/expectations/fail_with.rb +11 -6
  12. data/lib/rspec/expectations/failure_aggregator.rb +229 -0
  13. data/lib/rspec/expectations/handler.rb +36 -15
  14. data/lib/rspec/expectations/minitest_integration.rb +43 -2
  15. data/lib/rspec/expectations/syntax.rb +5 -5
  16. data/lib/rspec/expectations/version.rb +1 -1
  17. data/lib/rspec/expectations.rb +15 -1
  18. data/lib/rspec/matchers/aliased_matcher.rb +79 -4
  19. data/lib/rspec/matchers/built_in/all.rb +11 -0
  20. data/lib/rspec/matchers/built_in/base_matcher.rb +111 -28
  21. data/lib/rspec/matchers/built_in/be.rb +28 -114
  22. data/lib/rspec/matchers/built_in/be_between.rb +1 -1
  23. data/lib/rspec/matchers/built_in/be_instance_of.rb +5 -1
  24. data/lib/rspec/matchers/built_in/be_kind_of.rb +5 -1
  25. data/lib/rspec/matchers/built_in/be_within.rb +5 -12
  26. data/lib/rspec/matchers/built_in/change.rb +171 -63
  27. data/lib/rspec/matchers/built_in/compound.rb +201 -30
  28. data/lib/rspec/matchers/built_in/contain_exactly.rb +73 -12
  29. data/lib/rspec/matchers/built_in/count_expectation.rb +169 -0
  30. data/lib/rspec/matchers/built_in/eq.rb +3 -38
  31. data/lib/rspec/matchers/built_in/eql.rb +2 -2
  32. data/lib/rspec/matchers/built_in/equal.rb +3 -3
  33. data/lib/rspec/matchers/built_in/exist.rb +7 -3
  34. data/lib/rspec/matchers/built_in/has.rb +93 -30
  35. data/lib/rspec/matchers/built_in/have_attributes.rb +114 -0
  36. data/lib/rspec/matchers/built_in/include.rb +133 -25
  37. data/lib/rspec/matchers/built_in/match.rb +79 -2
  38. data/lib/rspec/matchers/built_in/operators.rb +14 -5
  39. data/lib/rspec/matchers/built_in/output.rb +59 -2
  40. data/lib/rspec/matchers/built_in/raise_error.rb +130 -27
  41. data/lib/rspec/matchers/built_in/respond_to.rb +117 -15
  42. data/lib/rspec/matchers/built_in/satisfy.rb +28 -14
  43. data/lib/rspec/matchers/built_in/{start_and_end_with.rb → start_or_end_with.rb} +20 -8
  44. data/lib/rspec/matchers/built_in/throw_symbol.rb +15 -5
  45. data/lib/rspec/matchers/built_in/yield.rb +129 -156
  46. data/lib/rspec/matchers/built_in.rb +5 -3
  47. data/lib/rspec/matchers/composable.rb +24 -36
  48. data/lib/rspec/matchers/dsl.rb +203 -37
  49. data/lib/rspec/matchers/english_phrasing.rb +58 -0
  50. data/lib/rspec/matchers/expecteds_for_multiple_diffs.rb +82 -0
  51. data/lib/rspec/matchers/fail_matchers.rb +42 -0
  52. data/lib/rspec/matchers/generated_descriptions.rb +1 -2
  53. data/lib/rspec/matchers/matcher_delegator.rb +3 -4
  54. data/lib/rspec/matchers/matcher_protocol.rb +105 -0
  55. data/lib/rspec/matchers.rb +267 -144
  56. data.tar.gz.sig +0 -0
  57. metadata +71 -49
  58. metadata.gz.sig +0 -0
  59. data/lib/rspec/matchers/pretty.rb +0 -77
@@ -21,6 +21,12 @@ module RSpec
21
21
  # `nil` is a valid value to pass.
22
22
  UndefinedValue = Module.new
23
23
 
24
+ # @note this name aligns with `Minitest::Expectation` so that our
25
+ # {InstanceMethods} module can be included in that class when
26
+ # used in a Minitest context.
27
+ # @return [Object] the target of the expectation
28
+ attr_reader :target
29
+
24
30
  # @api private
25
31
  def initialize(value)
26
32
  @target = value
@@ -36,43 +42,89 @@ module RSpec
36
42
  elsif block
37
43
  raise ArgumentError, "You cannot pass both an argument and a block to `expect`."
38
44
  else
39
- new(value)
45
+ ValueExpectationTarget.new(value)
46
+ end
47
+ end
48
+
49
+ # Defines instance {ExpectationTarget} instance methods. These are defined
50
+ # in a module so we can include it in `Minitest::Expectation` when
51
+ # `rspec/expectations/minitest_integration` is loaded in order to
52
+ # support usage with Minitest.
53
+ module InstanceMethods
54
+ # Runs the given expectation, passing if `matcher` returns true.
55
+ # @example
56
+ # expect(value).to eq(5)
57
+ # expect { perform }.to raise_error
58
+ # @param [Matcher]
59
+ # matcher
60
+ # @param [String, Proc] message optional message to display when the expectation fails
61
+ # @return [Boolean] true if the expectation succeeds (else raises)
62
+ # @see RSpec::Matchers
63
+ def to(matcher=nil, message=nil, &block)
64
+ prevent_operator_matchers(:to) unless matcher
65
+ RSpec::Expectations::PositiveExpectationHandler.handle_matcher(target, matcher, message, &block)
66
+ end
67
+
68
+ # Runs the given expectation, passing if `matcher` returns false.
69
+ # @example
70
+ # expect(value).not_to eq(5)
71
+ # @param [Matcher]
72
+ # matcher
73
+ # @param [String, Proc] message optional message to display when the expectation fails
74
+ # @return [Boolean] false if the negative expectation succeeds (else raises)
75
+ # @see RSpec::Matchers
76
+ def not_to(matcher=nil, message=nil, &block)
77
+ prevent_operator_matchers(:not_to) unless matcher
78
+ RSpec::Expectations::NegativeExpectationHandler.handle_matcher(target, matcher, message, &block)
79
+ end
80
+ alias to_not not_to
81
+
82
+ private
83
+
84
+ def prevent_operator_matchers(verb)
85
+ raise ArgumentError, "The expect syntax does not support operator matchers, " \
86
+ "so you must pass a matcher to `##{verb}`."
40
87
  end
41
88
  end
42
89
 
43
- # Runs the given expectation, passing if `matcher` returns true.
44
- # @example
45
- # expect(value).to eq(5)
46
- # expect { perform }.to raise_error
47
- # @param [Matcher]
48
- # matcher
49
- # @param [String or Proc] message optional message to display when the expectation fails
50
- # @return [Boolean] true if the expectation succeeds (else raises)
51
- # @see RSpec::Matchers
90
+ include InstanceMethods
91
+ end
92
+
93
+ # @private
94
+ # Validates the provided matcher to ensure it supports block
95
+ # expectations, in order to avoid user confusion when they
96
+ # use a block thinking the expectation will be on the return
97
+ # value of the block rather than the block itself.
98
+ class ValueExpectationTarget < ExpectationTarget
52
99
  def to(matcher=nil, message=nil, &block)
53
- prevent_operator_matchers(:to) unless matcher
54
- RSpec::Expectations::PositiveExpectationHandler.handle_matcher(@target, matcher, message, &block)
100
+ enforce_value_expectation(matcher)
101
+ super
55
102
  end
56
103
 
57
- # Runs the given expectation, passing if `matcher` returns false.
58
- # @example
59
- # expect(value).not_to eq(5)
60
- # @param [Matcher]
61
- # matcher
62
- # @param [String or Proc] message optional message to display when the expectation fails
63
- # @return [Boolean] false if the negative expectation succeeds (else raises)
64
- # @see RSpec::Matchers
65
104
  def not_to(matcher=nil, message=nil, &block)
66
- prevent_operator_matchers(:not_to) unless matcher
67
- RSpec::Expectations::NegativeExpectationHandler.handle_matcher(@target, matcher, message, &block)
105
+ enforce_value_expectation(matcher)
106
+ super
68
107
  end
69
- alias to_not not_to
70
108
 
71
109
  private
72
110
 
73
- def prevent_operator_matchers(verb)
74
- raise ArgumentError, "The expect syntax does not support operator matchers, " \
75
- "so you must pass a matcher to `##{verb}`."
111
+ def enforce_value_expectation(matcher)
112
+ return if supports_value_expectations?(matcher)
113
+
114
+ RSpec.deprecate(
115
+ "expect(value).to #{RSpec::Support::ObjectFormatter.format(matcher)}",
116
+ :message =>
117
+ "The implicit block expectation syntax is deprecated, you should pass " \
118
+ "a block rather than an argument to `expect` to use the provided " \
119
+ "block expectation matcher or the matcher must implement " \
120
+ "`supports_value_expectations?`. e.g `expect { value }.to " \
121
+ "#{RSpec::Support::ObjectFormatter.format(matcher)}` not " \
122
+ "`expect(value).to #{RSpec::Support::ObjectFormatter.format(matcher)}`"
123
+ )
124
+ end
125
+
126
+ def supports_value_expectations?(matcher)
127
+ !matcher.respond_to?(:supports_value_expectations?) || matcher.supports_value_expectations?
76
128
  end
77
129
  end
78
130
 
@@ -98,21 +150,13 @@ module RSpec
98
150
  def enforce_block_expectation(matcher)
99
151
  return if supports_block_expectations?(matcher)
100
152
 
101
- raise ExpectationNotMetError, "You must pass an argument rather than " \
102
- "a block to use the provided matcher (#{description_of matcher}), or " \
103
- "the matcher must implement `supports_block_expectations?`."
153
+ raise ExpectationNotMetError, "You must pass an argument rather than a block to `expect` to use the provided " \
154
+ "matcher (#{RSpec::Support::ObjectFormatter.format(matcher)}), or the matcher must implement " \
155
+ "`supports_block_expectations?`."
104
156
  end
105
157
 
106
158
  def supports_block_expectations?(matcher)
107
- matcher.supports_block_expectations?
108
- rescue NoMethodError
109
- false
110
- end
111
-
112
- def description_of(matcher)
113
- matcher.description
114
- rescue NoMethodError
115
- matcher.inspect
159
+ matcher.respond_to?(:supports_block_expectations?) && matcher.supports_block_expectations?
116
160
  end
117
161
  end
118
162
  end
@@ -1,12 +1,18 @@
1
- RSpec::Support.require_rspec_support 'differ'
2
-
3
1
  module RSpec
4
2
  module Expectations
5
3
  class << self
4
+ # @private
5
+ class Differ
6
+ # @private
7
+ OBJECT_PREPARER = lambda do |object|
8
+ RSpec::Matchers::Composable.surface_descriptions_in(object)
9
+ end
10
+ end
11
+
6
12
  # @private
7
13
  def differ
8
14
  RSpec::Support::Differ.new(
9
- :object_preparer => lambda { |object| RSpec::Matchers::Composable.surface_descriptions_in(object) },
15
+ :object_preparer => Differ::OBJECT_PREPARER,
10
16
  :color => RSpec::Matchers.configuration.color?
11
17
  )
12
18
  end
@@ -24,10 +30,9 @@ module RSpec
24
30
  "appropriate failure_message[_when_negated] method to return a string?"
25
31
  end
26
32
 
27
- diff = differ.diff(actual, expected)
28
- message = "#{message}\nDiff:#{diff}" unless diff.empty?
33
+ message = ::RSpec::Matchers::ExpectedsForMultipleDiffs.from(expected).message_with_diff(message, differ, actual)
29
34
 
30
- raise RSpec::Expectations::ExpectationNotMetError, message
35
+ RSpec::Support.notify_failure(RSpec::Expectations::ExpectationNotMetError.new message)
31
36
  end
32
37
  end
33
38
  end
@@ -0,0 +1,229 @@
1
+ module RSpec
2
+ module Expectations
3
+ # @private
4
+ class FailureAggregator
5
+ attr_reader :block_label, :metadata
6
+
7
+ # @private
8
+ class AggregatedFailure
9
+ # @private
10
+ MESSAGE =
11
+ 'AggregatedFailure: This method caused a failure which has been ' \
12
+ 'suppressed to be aggregated into our failure report by returning ' \
13
+ 'this value, further errors can be ignored.'
14
+
15
+ def inspect
16
+ MESSAGE
17
+ end
18
+ end
19
+
20
+ AGGREGATED_FAILURE = AggregatedFailure.new
21
+
22
+ def aggregate
23
+ RSpec::Support.with_failure_notifier(self) do
24
+ begin
25
+ yield
26
+ rescue ExpectationNotMetError => e
27
+ # Normally, expectation failures will be notified via the `call` method, below,
28
+ # but since the failure notifier uses a thread local variable, failing expectations
29
+ # in another thread will still raise. We handle that here and categorize it as part
30
+ # of `failures` rather than letting it fall through and be categorized as part of
31
+ # `other_errors`.
32
+ failures << e
33
+ rescue Support::AllExceptionsExceptOnesWeMustNotRescue => e
34
+ # While it is normally a bad practice to rescue `Exception`, it's important we do
35
+ # so here. It's low risk (`notify_aggregated_failures` below will re-raise the exception,
36
+ # or raise a `MultipleExpectationsNotMetError` that includes the exception), and it's
37
+ # essential that the user is notified of expectation failures that may have already
38
+ # occurred in the `aggregate_failures` block. Those expectation failures may provide
39
+ # important diagnostics for understanding why this exception occurred, and if we simply
40
+ # allowed this exception to be raised as-is, it would (wrongly) suggest to the user
41
+ # that the expectation passed when it did not, which would be quite confusing.
42
+ other_errors << e
43
+ end
44
+ end
45
+
46
+ notify_aggregated_failures
47
+ end
48
+
49
+ def failures
50
+ @failures ||= []
51
+ end
52
+
53
+ def other_errors
54
+ @other_errors ||= []
55
+ end
56
+
57
+ # This method is defined to satisfy the callable interface
58
+ # expected by `RSpec::Support.with_failure_notifier`.
59
+ def call(failure, options)
60
+ source_id = options[:source_id]
61
+ return if source_id && @seen_source_ids.key?(source_id)
62
+
63
+ @seen_source_ids[source_id] = true
64
+ assign_backtrace(failure) unless failure.backtrace
65
+ failures << failure
66
+
67
+ AGGREGATED_FAILURE
68
+ end
69
+
70
+ private
71
+
72
+ if RSpec::Support::Ruby.jruby? && RSpec::Support::Ruby.jruby_version < '9.2.0.0'
73
+ # On JRuby 9.1.x.x and before, `caller` and `raise` produce different backtraces with
74
+ # regards to `.java` stack frames. It's important that we use `raise` for JRuby to produce
75
+ # a backtrace that has a continuous common section with the raised `MultipleExpectationsNotMetError`,
76
+ # so that rspec-core's truncation logic can work properly on it to list the backtrace
77
+ # relative to the `aggregate_failures` block.
78
+ def assign_backtrace(failure)
79
+ raise failure
80
+ rescue failure.class => e
81
+ failure.set_backtrace(e.backtrace)
82
+ end
83
+ else
84
+ # Using `caller` performs better (and is simpler) than `raise` on most Rubies.
85
+ def assign_backtrace(failure)
86
+ failure.set_backtrace(caller)
87
+ end
88
+ end
89
+
90
+ def initialize(block_label, metadata)
91
+ @block_label = block_label
92
+ @metadata = metadata
93
+ @seen_source_ids = {} # don't want to load stdlib set
94
+ end
95
+
96
+ def notify_aggregated_failures
97
+ all_errors = failures + other_errors
98
+
99
+ case all_errors.size
100
+ when 0 then return true
101
+ when 1 then RSpec::Support.notify_failure all_errors.first
102
+ else RSpec::Support.notify_failure MultipleExpectationsNotMetError.new(self)
103
+ end
104
+ end
105
+ end
106
+
107
+ # Exception raised from `aggregate_failures` when multiple expectations fail.
108
+ class MultipleExpectationsNotMetError
109
+ # @return [String] The fully formatted exception message.
110
+ def message
111
+ @message ||= (["#{summary}:"] + enumerated_failures + enumerated_errors).join("\n\n")
112
+ end
113
+
114
+ # @return [Array<RSpec::Expectations::ExpectationNotMetError>] The list of expectation failures.
115
+ def failures
116
+ @failure_aggregator.failures
117
+ end
118
+
119
+ # @return [Array<Exception>] The list of other exceptions.
120
+ def other_errors
121
+ @failure_aggregator.other_errors
122
+ end
123
+
124
+ # @return [Array<Exception>] The list of expectation failures and other exceptions, combined.
125
+ attr_reader :all_exceptions
126
+
127
+ # @return [String] The user-assigned label for the aggregation block.
128
+ def aggregation_block_label
129
+ @failure_aggregator.block_label
130
+ end
131
+
132
+ # @return [Hash] The metadata hash passed to `aggregate_failures`.
133
+ def aggregation_metadata
134
+ @failure_aggregator.metadata
135
+ end
136
+
137
+ # @return [String] A summary of the failure, including the block label and a count of failures.
138
+ def summary
139
+ "Got #{exception_count_description} from failure aggregation " \
140
+ "block#{block_description}"
141
+ end
142
+
143
+ # return [String] A description of the failure/error counts.
144
+ def exception_count_description
145
+ failure_count = pluralize("failure", failures.size)
146
+ return failure_count if other_errors.empty?
147
+ error_count = pluralize("other error", other_errors.size)
148
+ "#{failure_count} and #{error_count}"
149
+ end
150
+
151
+ private
152
+
153
+ def initialize(failure_aggregator)
154
+ @failure_aggregator = failure_aggregator
155
+ @all_exceptions = failures + other_errors
156
+ end
157
+
158
+ def block_description
159
+ return "" unless aggregation_block_label
160
+ " #{aggregation_block_label.inspect}"
161
+ end
162
+
163
+ def pluralize(noun, count)
164
+ "#{count} #{noun}#{'s' unless count == 1}"
165
+ end
166
+
167
+ def enumerated(exceptions, index_offset)
168
+ exceptions.each_with_index.map do |exception, index|
169
+ index += index_offset
170
+ formatted_message = "#{yield exception}\n#{format_backtrace(exception.backtrace).first}"
171
+ "#{index_label index}#{indented formatted_message, index}"
172
+ end
173
+ end
174
+
175
+ def exclusion_patterns
176
+ patterns = %w[/lib\d*/ruby/ bin/ exe/rspec /lib/bundler/ /exe/bundle:]
177
+ patterns << "org/jruby/" if RSpec::Support::Ruby.jruby?
178
+ patterns.map! { |s| Regexp.new(s.gsub('/', File::SEPARATOR)) }
179
+ end
180
+
181
+ def format_backtrace(backtrace)
182
+ backtrace.map { |l| backtrace_line(l) }.compact.tap { |filtered| filtered.concat backtrace if filtered.empty? }
183
+ end
184
+
185
+ def backtrace_line(line)
186
+ return if [Regexp.union(RSpec::CallerFilter::IGNORE_REGEX, *exclusion_patterns)].any? { |p| line =~ p }
187
+
188
+ # It changes the current path that is relative to
189
+ # system root to be relative to the project root.
190
+ line.sub(/(\A|\s)#{File.expand_path('.')}(#{File::SEPARATOR}|\s|\Z)/, '\\1.\\2'.freeze).sub(/\A([^:]+:\d+)$/, '\\1'.freeze)
191
+ end
192
+
193
+ def enumerated_failures
194
+ enumerated(failures, 0, &:message)
195
+ end
196
+
197
+ def enumerated_errors
198
+ enumerated(other_errors, failures.size) do |error|
199
+ "#{error.class}: #{error.message}"
200
+ end
201
+ end
202
+
203
+ def indented(failure_message, index)
204
+ line_1, *rest = failure_message.strip.lines.to_a
205
+ first_line_indentation = ' ' * (longest_index_label_width - width_of_label(index))
206
+
207
+ first_line_indentation + line_1 + rest.map do |line|
208
+ line =~ /\S/ ? indentation + line : line
209
+ end.join
210
+ end
211
+
212
+ def indentation
213
+ @indentation ||= ' ' * longest_index_label_width
214
+ end
215
+
216
+ def longest_index_label_width
217
+ @longest_index_label_width ||= width_of_label(failures.size)
218
+ end
219
+
220
+ def width_of_label(index)
221
+ index_label(index).chars.count
222
+ end
223
+
224
+ def index_label(index)
225
+ " #{index + 1}) "
226
+ end
227
+ end
228
+ end
229
+ end
@@ -17,14 +17,17 @@ module RSpec
17
17
  #
18
18
  # @private
19
19
  def self.modern_matcher_from(matcher)
20
- LegacyMacherAdapter::RSpec2.wrap(matcher) ||
21
- LegacyMacherAdapter::RSpec1.wrap(matcher) || matcher
20
+ LegacyMatcherAdapter::RSpec2.wrap(matcher) ||
21
+ LegacyMatcherAdapter::RSpec1.wrap(matcher) || matcher
22
22
  end
23
23
 
24
- def self.setup(handler, matcher, message)
24
+ def self.with_matcher(handler, matcher, message)
25
25
  check_message(message)
26
+ matcher = modern_matcher_from(matcher)
27
+ yield matcher
28
+ ensure
26
29
  ::RSpec::Matchers.last_expectation_handler = handler
27
- ::RSpec::Matchers.last_matcher = modern_matcher_from(matcher)
30
+ ::RSpec::Matchers.last_matcher = matcher
28
31
  end
29
32
 
30
33
  def self.handle_failure(matcher, message, failure_message_method)
@@ -41,15 +44,21 @@ module RSpec
41
44
 
42
45
  # @private
43
46
  class PositiveExpectationHandler
44
- def self.handle_matcher(actual, initial_matcher, message=nil, &block)
45
- matcher = ExpectationHelper.setup(self, initial_matcher, message)
47
+ def self.handle_matcher(actual, initial_matcher, custom_message=nil, &block)
48
+ ExpectationHelper.with_matcher(self, initial_matcher, custom_message) do |matcher|
49
+ return ::RSpec::Matchers::BuiltIn::PositiveOperatorMatcher.new(actual) unless initial_matcher
46
50
 
47
- return ::RSpec::Matchers::BuiltIn::PositiveOperatorMatcher.new(actual) unless initial_matcher
48
- matcher.matches?(actual, &block) || ExpectationHelper.handle_failure(matcher, message, :failure_message)
51
+ match_result = matcher.matches?(actual, &block)
52
+ if custom_message && match_result.respond_to?(:error_generator)
53
+ match_result.error_generator.opts[:message] = custom_message
54
+ end
55
+
56
+ match_result || ExpectationHelper.handle_failure(matcher, custom_message, :failure_message)
57
+ end
49
58
  end
50
59
 
51
60
  def self.verb
52
- "should"
61
+ 'is expected to'
53
62
  end
54
63
 
55
64
  def self.should_method
@@ -63,11 +72,17 @@ module RSpec
63
72
 
64
73
  # @private
65
74
  class NegativeExpectationHandler
66
- def self.handle_matcher(actual, initial_matcher, message=nil, &block)
67
- matcher = ExpectationHelper.setup(self, initial_matcher, message)
75
+ def self.handle_matcher(actual, initial_matcher, custom_message=nil, &block)
76
+ ExpectationHelper.with_matcher(self, initial_matcher, custom_message) do |matcher|
77
+ return ::RSpec::Matchers::BuiltIn::NegativeOperatorMatcher.new(actual) unless initial_matcher
78
+
79
+ negated_match_result = does_not_match?(matcher, actual, &block)
80
+ if custom_message && negated_match_result.respond_to?(:error_generator)
81
+ negated_match_result.error_generator.opts[:message] = custom_message
82
+ end
68
83
 
69
- return ::RSpec::Matchers::BuiltIn::NegativeOperatorMatcher.new(actual) unless initial_matcher
70
- !(does_not_match?(matcher, actual, &block) || ExpectationHelper.handle_failure(matcher, message, :failure_message_when_negated))
84
+ negated_match_result || ExpectationHelper.handle_failure(matcher, custom_message, :failure_message_when_negated)
85
+ end
71
86
  end
72
87
 
73
88
  def self.does_not_match?(matcher, actual, &block)
@@ -79,7 +94,7 @@ module RSpec
79
94
  end
80
95
 
81
96
  def self.verb
82
- "should not"
97
+ 'is expected not to'
83
98
  end
84
99
 
85
100
  def self.should_method
@@ -95,7 +110,7 @@ module RSpec
95
110
  # order to present the current protocol.
96
111
  #
97
112
  # @private
98
- class LegacyMacherAdapter < Matchers::MatcherDelegator
113
+ class LegacyMatcherAdapter < Matchers::MatcherDelegator
99
114
  def initialize(matcher)
100
115
  super
101
116
  ::RSpec.warn_deprecation(<<-EOS.gsub(/^\s+\|/, ''), :type => "legacy_matcher")
@@ -157,5 +172,11 @@ module RSpec
157
172
  end
158
173
  end
159
174
  end
175
+
176
+ # RSpec 3.0 was released with the class name misspelled. For SemVer compatibility,
177
+ # we will provide this misspelled alias until 4.0.
178
+ # @deprecated Use LegacyMatcherAdapter instead.
179
+ # @private
180
+ LegacyMacherAdapter = LegacyMatcherAdapter
160
181
  end
161
182
  end
@@ -3,15 +3,56 @@ require 'rspec/expectations'
3
3
  Minitest::Test.class_eval do
4
4
  include ::RSpec::Matchers
5
5
 
6
+ # This `expect` will only be called if the user is using Minitest < 5.6
7
+ # or if they are _not_ using Minitest::Spec on 5.6+. Minitest::Spec on 5.6+
8
+ # defines its own `expect` and will have the assertions incremented via our
9
+ # definitions of `to`/`not_to`/`to_not` below.
6
10
  def expect(*a, &b)
7
- assert(true) # so each expectation gets counted in minitest's assertion stats
11
+ self.assertions += 1
8
12
  super
9
13
  end
14
+
15
+ # Convert a `MultipleExpectationsNotMetError` to a `Minitest::Assertion` error so
16
+ # it gets counted in minitest's summary stats as a failure rather than an error.
17
+ # It would be nice to make `MultipleExpectationsNotMetError` subclass
18
+ # `Minitest::Assertion`, but Minitest's implementation does not treat subclasses
19
+ # the same, so this is the best we can do.
20
+ def aggregate_failures(*args, &block)
21
+ super
22
+ rescue RSpec::Expectations::MultipleExpectationsNotMetError => e
23
+ assertion_failed = Minitest::Assertion.new(e.message)
24
+ assertion_failed.set_backtrace e.backtrace
25
+ raise assertion_failed
26
+ end
27
+ end
28
+
29
+ # Older versions of Minitest (e.g. before 5.6) do not define
30
+ # `Minitest::Expectation`.
31
+ if defined?(::Minitest::Expectation)
32
+ Minitest::Expectation.class_eval do
33
+ include RSpec::Expectations::ExpectationTarget::InstanceMethods
34
+
35
+ def to(*args)
36
+ ctx.assertions += 1
37
+ super
38
+ end
39
+
40
+ def not_to(*args)
41
+ ctx.assertions += 1
42
+ super
43
+ end
44
+
45
+ def to_not(*args)
46
+ ctx.assertions += 1
47
+ super
48
+ end
49
+ end
10
50
  end
11
51
 
12
52
  module RSpec
13
53
  module Expectations
14
54
  remove_const :ExpectationNotMetError
15
- ExpectationNotMetError = ::Minitest::Assertion
55
+ # Exception raised when an expectation fails.
56
+ const_set :ExpectationNotMetError, ::Minitest::Assertion
16
57
  end
17
58
  end
@@ -27,7 +27,7 @@ module RSpec
27
27
 
28
28
  RSpec.deprecate(
29
29
  "Using `#{method_name}` from rspec-expectations' old `:should` syntax without explicitly enabling the syntax",
30
- :replacement => "the new `:expect` syntax or explicitly enable `:should`"
30
+ :replacement => "the new `:expect` syntax or explicitly enable `:should` with `config.expect_with(:rspec) { |c| c.syntax = :should }`"
31
31
  )
32
32
 
33
33
  @warn_about_should = false
@@ -41,12 +41,12 @@ module RSpec
41
41
 
42
42
  syntax_host.module_exec do
43
43
  def should(matcher=nil, message=nil, &block)
44
- ::RSpec::Expectations::Syntax.warn_about_should_unless_configured(__method__)
44
+ ::RSpec::Expectations::Syntax.warn_about_should_unless_configured(::Kernel.__method__)
45
45
  ::RSpec::Expectations::PositiveExpectationHandler.handle_matcher(self, matcher, message, &block)
46
46
  end
47
47
 
48
48
  def should_not(matcher=nil, message=nil, &block)
49
- ::RSpec::Expectations::Syntax.warn_about_should_unless_configured(__method__)
49
+ ::RSpec::Expectations::Syntax.warn_about_should_unless_configured(::Kernel.__method__)
50
50
  ::RSpec::Expectations::NegativeExpectationHandler.handle_matcher(self, matcher, message, &block)
51
51
  end
52
52
  end
@@ -106,7 +106,7 @@ if defined?(BasicObject)
106
106
  # that this syntax does not always play nice with delegate/proxy objects.
107
107
  # We recommend you use the non-monkeypatching `:expect` syntax instead.
108
108
  class BasicObject
109
- # @method should
109
+ # @method should(matcher, message)
110
110
  # Passes if `matcher` returns true. Available on every `Object`.
111
111
  # @example
112
112
  # actual.should eq expected
@@ -118,7 +118,7 @@ if defined?(BasicObject)
118
118
  # @note This is only available when you have enabled the `:should` syntax.
119
119
  # @see RSpec::Matchers
120
120
 
121
- # @method should_not
121
+ # @method should_not(matcher, message)
122
122
  # Passes if `matcher` returns false. Available on every `Object`.
123
123
  # @example
124
124
  # actual.should_not eq expected
@@ -2,7 +2,7 @@ module RSpec
2
2
  module Expectations
3
3
  # @private
4
4
  module Version
5
- STRING = '3.0.4'
5
+ STRING = '3.12.3'
6
6
  end
7
7
  end
8
8
  end
@@ -1,6 +1,7 @@
1
1
  require 'rspec/support'
2
2
  RSpec::Support.require_rspec_support "caller_filter"
3
3
  RSpec::Support.require_rspec_support "warnings"
4
+ RSpec::Support.require_rspec_support "object_formatter"
4
5
 
5
6
  require 'rspec/matchers'
6
7
 
@@ -63,6 +64,19 @@ module RSpec
63
64
  # the user sets an expectation, it can't be caught in their
64
65
  # code by a bare `rescue`.
65
66
  # @api public
66
- ExpectationNotMetError = Class.new(::Exception)
67
+ class ExpectationNotMetError < Exception
68
+ end
69
+
70
+ # Exception raised from `aggregate_failures` when multiple expectations fail.
71
+ #
72
+ # @note The constant is defined here but the extensive logic of this class
73
+ # is lazily defined when `FailureAggregator` is autoloaded, since we do
74
+ # not need to waste time defining that functionality unless
75
+ # `aggregate_failures` is used.
76
+ class MultipleExpectationsNotMetError < ExpectationNotMetError
77
+ end
78
+
79
+ autoload :BlockSnippetExtractor, "rspec/expectations/block_snippet_extractor"
80
+ autoload :FailureAggregator, "rspec/expectations/failure_aggregator"
67
81
  end
68
82
  end