rspec-expectations 3.2.1 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Changelog.md +55 -4
  5. data/README.md +1 -1
  6. data/lib/rspec/expectations.rb +13 -1
  7. data/lib/rspec/expectations/configuration.rb +17 -0
  8. data/lib/rspec/expectations/expectation_target.rb +3 -9
  9. data/lib/rspec/expectations/fail_with.rb +1 -3
  10. data/lib/rspec/expectations/failure_aggregator.rb +194 -0
  11. data/lib/rspec/expectations/minitest_integration.rb +13 -0
  12. data/lib/rspec/expectations/version.rb +1 -1
  13. data/lib/rspec/matchers.rb +59 -5
  14. data/lib/rspec/matchers/built_in/base_matcher.rb +56 -7
  15. data/lib/rspec/matchers/built_in/be.rb +25 -15
  16. data/lib/rspec/matchers/built_in/be_between.rb +1 -1
  17. data/lib/rspec/matchers/built_in/be_within.rb +2 -2
  18. data/lib/rspec/matchers/built_in/contain_exactly.rb +12 -8
  19. data/lib/rspec/matchers/built_in/eq.rb +3 -38
  20. data/lib/rspec/matchers/built_in/eql.rb +2 -2
  21. data/lib/rspec/matchers/built_in/equal.rb +3 -3
  22. data/lib/rspec/matchers/built_in/exist.rb +2 -2
  23. data/lib/rspec/matchers/built_in/has.rb +3 -1
  24. data/lib/rspec/matchers/built_in/have_attributes.rb +5 -4
  25. data/lib/rspec/matchers/built_in/include.rb +44 -19
  26. data/lib/rspec/matchers/built_in/match.rb +9 -1
  27. data/lib/rspec/matchers/built_in/operators.rb +14 -5
  28. data/lib/rspec/matchers/built_in/output.rb +9 -2
  29. data/lib/rspec/matchers/built_in/raise_error.rb +64 -22
  30. data/lib/rspec/matchers/built_in/respond_to.rb +2 -3
  31. data/lib/rspec/matchers/built_in/satisfy.rb +7 -9
  32. data/lib/rspec/matchers/built_in/start_or_end_with.rb +3 -1
  33. data/lib/rspec/matchers/built_in/throw_symbol.rb +1 -1
  34. data/lib/rspec/matchers/built_in/yield.rb +7 -5
  35. data/lib/rspec/matchers/composable.rb +5 -4
  36. data/lib/rspec/matchers/dsl.rb +19 -6
  37. data/lib/rspec/matchers/english_phrasing.rb +42 -0
  38. data/lib/rspec/matchers/expecteds_for_multiple_diffs.rb +2 -8
  39. data/lib/rspec/matchers/fail_matchers.rb +42 -0
  40. data/lib/rspec/matchers/matcher_delegator.rb +2 -0
  41. metadata +9 -7
  42. metadata.gz.sig +0 -0
  43. data/lib/rspec/matchers/pretty.rb +0 -77
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3aa831b7183b5d522f5e67b19053832a7adeec29
4
- data.tar.gz: fef8e60148530b9df3545e113341083c426baa56
3
+ metadata.gz: 050cebd3fe559b7cd5391cba39333fc9815e7650
4
+ data.tar.gz: 0034049cdc0f2fd7ab5210ba606b72a267b3abbc
5
5
  SHA512:
6
- metadata.gz: e51ebb3d7564f575eee701467eda6e003fade12c2644b38b6b8eba5d2820bccfe60ab22d8ae410e76bd0e5c18dfdbd94298108699f5d4d6a12ee61fb65f03db1
7
- data.tar.gz: 75f4ea4fd9cf63a31967aa3cceb6071de46dd57c3326fe8cd35d47710c741d45d36ba2b77593236d2f614a0b1e921c6a21e08b60c41cd23b7431e77835d68248
6
+ metadata.gz: afee08bfc1eedff07267817c53989802b649cab426c15f55aa869e71bac7db27eadb679b854d5c9da3bbdb7ac39bedcc7c448e8fd7ae8eec311b1a19a694ff07
7
+ data.tar.gz: 1215a16c5ec70686dba0fd02d4ae14c4238352a995850a4c437eda0d7a4279597e1866f7426c623afc31ba32db5215517102576a2442caeddfdc44da308e5123
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -1,3 +1,54 @@
1
+ ### 3.3.0 / 2015-06-12
2
+ [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.2.1...v3.3.0)
3
+
4
+ Enhancements:
5
+
6
+ * Expose `RSpec::Matchers::EnglishPhrasing` to make it easier to write
7
+ nice failure messages in custom matchers. (Jared Beck, #736)
8
+ * Add `RSpec::Matchers::FailMatchers`, a mixin which provides
9
+ `fail`, `fail_with` and `fail_including` matchers for use in
10
+ specifying that an expectation fails for use by
11
+ extension/plugin authors. (Charlie Rudolph, #729)
12
+ * Avoid loading `tempfile` (and its dependencies) unless
13
+ it is absolutely needed. (Myron Marston, #735)
14
+ * Improve failure output when attempting to use `be_true` or `be_false`.
15
+ (Tim Wade, #744)
16
+ * Define `RSpec::Matchers#respond_to_missing?` so that
17
+ `RSpec::Matchers#respond_to?` and `RSpec::Matchers#method` handle
18
+ dynamic predicate matchers. (Andrei Botalov, #751)
19
+ * Use custom Time/DateTime/BigDecimal formatting for all matchers
20
+ so they are consistently represented in failure messages.
21
+ (Gavin Miller, #740)
22
+ * Add configuration to turn off warnings about matcher combinations that
23
+ may cause false positives. (Jon Rowe, #768)
24
+ * Warn when using a bare `raise_error` matcher that you may be subject to
25
+ false positives. (Jon Rowe, #768)
26
+ * Warn rather than raise when using the`raise_error` matcher in negative
27
+ expectations that may be subject to false positives. (Jon Rowe, #775)
28
+ * Improve failure message for `include(a, b, c)` so that if `a` and `b`
29
+ are included the failure message only mentions `c`. (Chris Arcand, #780)
30
+ * Allow `satisfy` matcher to take an optional description argument
31
+ that will be used in the `description`, `failure_message` and
32
+ `failure_message_when_negated` in place of the undescriptive
33
+ "sastify block". (Chris Arcand, #783)
34
+ * Add new `aggregate_failures` API that allows multiple independent
35
+ expectations to all fail and be listed in the failure output, rather
36
+ than the example aborting on the first failure. (Myron Marston, #776)
37
+ * Improve `raise_error` matcher so that it can accept a matcher as a single argument
38
+ that matches the message. (Time Wade, #782)
39
+
40
+ Bug Fixes:
41
+
42
+ * Make `contain_exactly` / `match_array` work with strict test doubles
43
+ that have not defined `<=>`. (Myron Marston, #758)
44
+ * Fix `include` matcher so that it omits the diff when it would
45
+ confusingly highlight items that are actually included but are not
46
+ an exact match in a line-by-line diff. (Tim Wade, #763)
47
+ * Fix `match` matcher so that it does not blow up when matching a string
48
+ or regex against another matcher (rather than a string or regex).
49
+ (Myron Marston, #772)
50
+ * Silence whitespace-only diffs. (Myron Marston, #801)
51
+
1
52
  ### 3.2.1 / 2015-04-06
2
53
  [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.2.0...v3.2.1)
3
54
 
@@ -321,7 +372,7 @@ Breaking Changes for 3.0.0:
321
372
  (Sam Phippen)
322
373
  * Remove the deprecated `have`, `have_at_least` and `have_at_most` matchers.
323
374
  You can continue using those matchers through https://github.com/rspec/rspec-collection_matchers,
324
- or you can rewrite your expectations with something like
375
+ or you can rewrite your expectations with something like
325
376
  `expect(your_object.size).to eq(num)`. (Hugo Baraúna)
326
377
  * Rename `be_true` and `be_false` to `be_truthy` and `be_falsey`. (Sam Phippen)
327
378
  * Make `expect { }.to_not raise_error(SomeSpecificClass, message)`,
@@ -442,9 +493,9 @@ Deprecations:
442
493
  Deprecations
443
494
 
444
495
  * Deprecate `have`, `have_at_least` and `have_at_most`. You can continue using those
445
- matchers through https://github.com/rspec/rspec-collection_matchers, or
446
- you can rewrite your expectations with something like
447
- `expect(your_object.size).to eq(num)`. (Hugo Baraúna)
496
+ matchers through https://github.com/rspec/rspec-collection_matchers, or
497
+ you can rewrite your expectations with something like
498
+ `expect(your_object.size).to eq(num)`. (Hugo Baraúna)
448
499
  * Deprecate `be_xyz` predicate matcher when `xyz?` is a private method.
449
500
  (Jon Rowe)
450
501
  * Deprecate `be_true`/`be_false` in favour of `be_truthy`/`be_falsey`
data/README.md CHANGED
@@ -95,7 +95,7 @@ Note: The new `expect` syntax no longer supports the `=~` matcher.
95
95
 
96
96
  ```ruby
97
97
  expect(actual).to be_an_instance_of(expected) # passes if actual.class == expected
98
- expect(actual).to be_a(expected) # passes if actual.is_a?(expected)
98
+ expect(actual).to be_a(expected) # passes if actual.kind_of?(expected)
99
99
  expect(actual).to be_an(expected) # an alias for be_a
100
100
  expect(actual).to be_a_kind_of(expected) # another alias
101
101
  ```
@@ -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,7 +64,18 @@ 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
- class ExpectationNotMetError < ::Exception
67
+ class ExpectationNotMetError < Exception
67
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 :FailureAggregator, "rspec/expectations/failure_aggregator"
68
80
  end
69
81
  end
@@ -18,6 +18,10 @@ module RSpec
18
18
  #
19
19
  # RSpec::Expectations.configuration
20
20
  class Configuration
21
+ def initialize
22
+ @warn_about_potential_false_positives = true
23
+ end
24
+
21
25
  # Configures the supported syntax.
22
26
  # @param [Array<Symbol>, Symbol] values the syntaxes to enable
23
27
  # @example
@@ -133,6 +137,19 @@ module RSpec
133
137
  backtrace
134
138
  end
135
139
  end
140
+
141
+ # Configures whether RSpec will warn about matcher use which will
142
+ # potentially cause false positives in tests.
143
+ #
144
+ # @param value [Boolean]
145
+ attr_writer :warn_about_potential_false_positives
146
+
147
+ # Indicates whether RSpec will warn about matcher use which will
148
+ # potentially cause false positives in tests, generally you want to
149
+ # avoid such scenarios so this defaults to `true`.
150
+ def warn_about_potential_false_positives?
151
+ @warn_about_potential_false_positives
152
+ end
136
153
  end
137
154
 
138
155
  # The configuration object.
@@ -98,9 +98,9 @@ module RSpec
98
98
  def enforce_block_expectation(matcher)
99
99
  return if supports_block_expectations?(matcher)
100
100
 
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?`."
101
+ raise ExpectationNotMetError, "You must pass an argument rather than a block to use the provided " \
102
+ "matcher (#{RSpec::Support::ObjectFormatter.format(matcher)}), or the matcher must implement " \
103
+ "`supports_block_expectations?`."
104
104
  end
105
105
 
106
106
  def supports_block_expectations?(matcher)
@@ -108,12 +108,6 @@ module RSpec
108
108
  rescue NoMethodError
109
109
  false
110
110
  end
111
-
112
- def description_of(matcher)
113
- matcher.description
114
- rescue NoMethodError
115
- matcher.inspect
116
- end
117
111
  end
118
112
  end
119
113
  end
@@ -1,5 +1,3 @@
1
- RSpec::Support.require_rspec_support 'differ'
2
-
3
1
  module RSpec
4
2
  module Expectations
5
3
  class << self
@@ -26,7 +24,7 @@ module RSpec
26
24
 
27
25
  message = ::RSpec::Matchers::ExpectedsForMultipleDiffs.from(expected).message_with_diff(message, differ, actual)
28
26
 
29
- raise RSpec::Expectations::ExpectationNotMetError, message
27
+ RSpec::Support.notify_failure(RSpec::Expectations::ExpectationNotMetError.new message)
30
28
  end
31
29
  end
32
30
  end
@@ -0,0 +1,194 @@
1
+ module RSpec
2
+ module Expectations
3
+ # @private
4
+ class FailureAggregator
5
+ attr_reader :block_label, :metadata
6
+
7
+ def aggregate
8
+ RSpec::Support.with_failure_notifier(self) do
9
+ begin
10
+ yield
11
+ rescue ExpectationNotMetError => e
12
+ # Normally, expectation failures will be notified via the `call` method, below,
13
+ # but since the failure notifier uses a thread local variable, failing expectations
14
+ # in another thread will still raise. We handle that here and categorize it as part
15
+ # of `failures` rather than letting it fall through and be categorized as part of
16
+ # `other_errors`.
17
+ failures << e
18
+ rescue Exception => e
19
+ # While it is normally a bad practice to rescue `Exception`, it's important we do
20
+ # so here. It's low risk (`notify_aggregated_failures` below will re-raise the exception,
21
+ # or raise a `MultipleExpectationsNotMetError` that includes the exception), and it's
22
+ # essential that the user is notified of expectation failures that may have already
23
+ # occurred in the `aggregate_failures` block. Those expectation failures may provide
24
+ # important diagnostics for understanding why this exception occurred, and if we simply
25
+ # allowed this exception to be raised as-is, it would (wrongly) suggest to the user
26
+ # that the expectation passed when it did not, which would be quite confusing.
27
+ other_errors << e
28
+ end
29
+ end
30
+
31
+ notify_aggregated_failures
32
+ end
33
+
34
+ def failures
35
+ @failures ||= []
36
+ end
37
+
38
+ def other_errors
39
+ @other_errors ||= []
40
+ end
41
+
42
+ # This method is defined to satisfy the callable interface
43
+ # expected by `RSpec::Support.with_failure_notifier`.
44
+ def call(failure, options)
45
+ source_id = options[:source_id]
46
+ return if source_id && @seen_source_ids.key?(source_id)
47
+
48
+ @seen_source_ids[source_id] = true
49
+ assign_backtrace(failure) unless failure.backtrace
50
+ failures << failure
51
+ end
52
+
53
+ private
54
+
55
+ if RSpec::Support::Ruby.jruby?
56
+ # On JRuby, `caller` and `raise` produce different backtraces with regards to `.java`
57
+ # stack frames. It's important that we use `raise` for JRuby to produce a backtrace
58
+ # that has a continuous common section with the raised `MultipleExpectationsNotMetError`,
59
+ # so that rspec-core's truncation logic can work properly on it to list the backtrace
60
+ # relative to the `aggregate_failures` block.
61
+ def assign_backtrace(failure)
62
+ raise failure
63
+ rescue failure.class => e
64
+ failure.set_backtrace(e.backtrace)
65
+ end
66
+ else
67
+ # Using `caller` performs better (and is simpler) than `raise` on most Rubies.
68
+ def assign_backtrace(failure)
69
+ failure.set_backtrace(caller)
70
+ end
71
+ end
72
+
73
+ def initialize(block_label, metadata)
74
+ @block_label = block_label
75
+ @metadata = metadata
76
+ @seen_source_ids = {} # don't want to load stdlib set
77
+ end
78
+
79
+ def notify_aggregated_failures
80
+ all_errors = failures + other_errors
81
+
82
+ case all_errors.size
83
+ when 0 then return nil
84
+ when 1 then RSpec::Support.notify_failure all_errors.first
85
+ else RSpec::Support.notify_failure MultipleExpectationsNotMetError.new(self)
86
+ end
87
+ end
88
+ end
89
+
90
+ # Exception raised from `aggregate_failures` when multiple expectations fail.
91
+ class MultipleExpectationsNotMetError
92
+ # @return [String] The fully formatted exception message.
93
+ def message
94
+ @message ||= (["#{summary}:"] + enumerated_failures + enumerated_errors).join("\n\n")
95
+ end
96
+
97
+ # @return [Array<RSpec::Expectations::ExpectationNotMetError>] The list of expectation failures.
98
+ def failures
99
+ @failure_aggregator.failures
100
+ end
101
+
102
+ # @return [Array<Exception>] The list of other exceptions.
103
+ def other_errors
104
+ @failure_aggregator.other_errors
105
+ end
106
+
107
+ # @return [Array<Exception>] The list of expectation failures and other exceptions, combined.
108
+ attr_reader :all_exceptions
109
+
110
+ # @return [String] The user-assigned label for the aggregation block.
111
+ def aggregation_block_label
112
+ @failure_aggregator.block_label
113
+ end
114
+
115
+ # @return [Hash] The metadata hash passed to `aggregate_failures`.
116
+ def aggregation_metadata
117
+ @failure_aggregator.metadata
118
+ end
119
+
120
+ # @return [String] A summary of the failure, including the block label and a count of failures.
121
+ def summary
122
+ "Got #{exception_count_description} from failure aggregation " \
123
+ "block#{block_description}"
124
+ end
125
+
126
+ # return [String] A description of the failure/error counts.
127
+ def exception_count_description
128
+ failure_count = pluralize("failure", failures.size)
129
+ return failure_count if other_errors.empty?
130
+ error_count = pluralize("other error", other_errors.size)
131
+ "#{failure_count} and #{error_count}"
132
+ end
133
+
134
+ private
135
+
136
+ def initialize(failure_aggregator)
137
+ @failure_aggregator = failure_aggregator
138
+ @all_exceptions = failures + other_errors
139
+ end
140
+
141
+ def block_description
142
+ return "" unless aggregation_block_label
143
+ " #{aggregation_block_label.inspect}"
144
+ end
145
+
146
+ def pluralize(noun, count)
147
+ "#{count} #{noun}#{'s' unless count == 1}"
148
+ end
149
+
150
+ def enumerated(exceptions, index_offset)
151
+ exceptions.each_with_index.map do |exception, index|
152
+ index += index_offset
153
+ formatted_message = yield exception
154
+ "#{index_label index}#{indented formatted_message, index}"
155
+ end
156
+ end
157
+
158
+ def enumerated_failures
159
+ enumerated(failures, 0, &:message)
160
+ end
161
+
162
+ def enumerated_errors
163
+ enumerated(other_errors, failures.size) do |error|
164
+ "#{error.class}: #{error.message}"
165
+ end
166
+ end
167
+
168
+ def indented(failure_message, index)
169
+ line_1, *rest = failure_message.strip.lines.to_a
170
+ first_line_indentation = ' ' * (longest_index_label_width - width_of_label(index))
171
+
172
+ first_line_indentation + line_1 + rest.map do |line|
173
+ line =~ /\S/ ? indentation + line : line
174
+ end.join
175
+ end
176
+
177
+ def indentation
178
+ @indentation ||= ' ' * longest_index_label_width
179
+ end
180
+
181
+ def longest_index_label_width
182
+ @longest_index_label_width ||= width_of_label(failures.size)
183
+ end
184
+
185
+ def width_of_label(index)
186
+ index_label(index).chars.count
187
+ end
188
+
189
+ def index_label(index)
190
+ " #{index + 1}) "
191
+ end
192
+ end
193
+ end
194
+ end
@@ -7,6 +7,19 @@ Minitest::Test.class_eval do
7
7
  assert(true) # so each expectation gets counted in minitest's assertion stats
8
8
  super
9
9
  end
10
+
11
+ # Convert a `MultipleExpectationsNotMetError` to a `Minitest::Assertion` error so
12
+ # it gets counted in minitest's summary stats as a failure rather than an error.
13
+ # It would be nice to make `MultipleExpectationsNotMetError` subclass
14
+ # `Minitest::Assertion`, but Minitest's implementation does not treat subclasses
15
+ # the same, so this is the best we can do.
16
+ def aggregate_failures(*args, &block)
17
+ super
18
+ rescue RSpec::Expectations::MultipleExpectationsNotMetError => e
19
+ assertion_failed = Minitest::Assertion.new(e.message)
20
+ assertion_failed.set_backtrace e.backtrace
21
+ raise assertion_failed
22
+ end
10
23
  end
11
24
 
12
25
  module RSpec
@@ -2,7 +2,7 @@ module RSpec
2
2
  module Expectations
3
3
  # @private
4
4
  module Version
5
- STRING = '3.2.1'
5
+ STRING = '3.3.0'
6
6
  end
7
7
  end
8
8
  end
@@ -3,7 +3,7 @@ RSpec::Support.require_rspec_support 'matcher_definition'
3
3
  RSpec::Support.define_optimized_require_for_rspec(:matchers) { |f| require_relative(f) }
4
4
 
5
5
  %w[
6
- pretty
6
+ english_phrasing
7
7
  composable
8
8
  built_in
9
9
  generated_descriptions
@@ -243,7 +243,7 @@ module RSpec
243
243
  # alias $1 $2
244
244
  def self.alias_matcher(new_name, old_name, options={}, &description_override)
245
245
  description_override ||= lambda do |old_desc|
246
- old_desc.gsub(Pretty.split_words(old_name), Pretty.split_words(new_name))
246
+ old_desc.gsub(EnglishPhrasing.split_words(old_name), EnglishPhrasing.split_words(new_name))
247
247
  end
248
248
  klass = options.fetch(:klass) { AliasedMatcher }
249
249
 
@@ -277,6 +277,42 @@ module RSpec
277
277
  alias_matcher(negated_name, base_name, :klass => AliasedNegatedMatcher, &description_override)
278
278
  end
279
279
 
280
+ # Allows multiple expectations in the provided block to fail, and then
281
+ # aggregates them into a single exception, rather than aborting on the
282
+ # first expectation failure like normal. This allows you to see all
283
+ # failures from an entire set of expectations without splitting each
284
+ # off into its own example (which may slow things down if the example
285
+ # setup is expensive).
286
+ #
287
+ # @param label [String] label for this aggregation block, which will be
288
+ # included in the aggregated exception message.
289
+ # @param metadata [Hash] additional metadata about this failure aggregation
290
+ # block. If multiple expectations fail, it will be exposed from the
291
+ # {Expectations::MultipleExpectationsNotMetError} exception. Mostly
292
+ # intended for internal RSpec use but you can use it as well.
293
+ # @yield Block containing as many expectation as you want. The block is
294
+ # simply yielded to, so you can trust that anything that works outside
295
+ # the block should work within it.
296
+ # @raise [Expectations::MultipleExpectationsNotMetError] raised when
297
+ # multiple expectations fail.
298
+ # @raise [Expectations::ExpectationNotMetError] raised when a single
299
+ # expectation fails.
300
+ # @raise [Exception] other sorts of exceptions will be raised as normal.
301
+ #
302
+ # @example
303
+ # aggregate_failures("verifying response") do
304
+ # expect(response.status).to eq(200)
305
+ # expect(response.headers).to include("Content-Type" => "text/plain")
306
+ # expect(response.body).to include("Success")
307
+ # end
308
+ #
309
+ # @note The implementation of this feature uses a thread-local variable,
310
+ # which means that if you have an expectation failure in another thread,
311
+ # it'll abort like normal.
312
+ def aggregate_failures(label=nil, metadata={}, &block)
313
+ Expectations::FailureAggregator.new(label, metadata).aggregate(&block)
314
+ end
315
+
280
316
  # Passes if actual is truthy (anything but false or nil)
281
317
  def be_truthy
282
318
  BuiltIn::BeTruthy.new
@@ -732,7 +768,7 @@ module RSpec
732
768
  # expect { do_something_risky }.to raise_error(PoorRiskDecisionError, /oo ri/)
733
769
  #
734
770
  # expect { do_something_risky }.not_to raise_error
735
- def raise_error(error=Exception, message=nil, &block)
771
+ def raise_error(error=nil, message=nil, &block)
736
772
  BuiltIn::RaiseError.new(error, message, &block)
737
773
  end
738
774
  alias_method :raise_exception, :raise_error
@@ -767,10 +803,13 @@ module RSpec
767
803
  # If you do find yourself in such a situation, you could always write
768
804
  # a custom matcher, which would likely make your specs more expressive.
769
805
  #
806
+ # @param description [String] optional description to be used for this matcher.
807
+ #
770
808
  # @example
771
809
  # expect(5).to satisfy { |n| n > 3 }
772
- def satisfy(&block)
773
- BuiltIn::Satisfy.new(&block)
810
+ # expect(5).to satisfy("be greater than 3") { |n| n > 3 }
811
+ def satisfy(description="satisfy block", &block)
812
+ BuiltIn::Satisfy.new(description, &block)
774
813
  end
775
814
  alias_matcher :an_object_satisfying, :satisfy
776
815
  alias_matcher :satisfying, :satisfy
@@ -915,6 +954,7 @@ module RSpec
915
954
 
916
955
  BE_PREDICATE_REGEX = /^(be_(?:an?_)?)(.*)/
917
956
  HAS_REGEX = /^(?:have_)(.*)/
957
+ DYNAMIC_MATCHER_REGEX = Regexp.union(BE_PREDICATE_REGEX, HAS_REGEX)
918
958
 
919
959
  def method_missing(method, *args, &block)
920
960
  case method.to_s
@@ -927,6 +967,20 @@ module RSpec
927
967
  end
928
968
  end
929
969
 
970
+ if RUBY_VERSION.to_f >= 1.9
971
+ def respond_to_missing?(method, *)
972
+ method =~ DYNAMIC_MATCHER_REGEX || super
973
+ end
974
+ else # for 1.8.7
975
+ # :nocov:
976
+ def respond_to?(method, *)
977
+ method = method.to_s
978
+ method =~ DYNAMIC_MATCHER_REGEX || super
979
+ end
980
+ public :respond_to?
981
+ # :nocov:
982
+ end
983
+
930
984
  # @api private
931
985
  def self.is_a_matcher?(obj)
932
986
  return true if ::RSpec::Matchers::BuiltIn::BaseMatcher === obj