rspec-expectations 3.0.0.beta2 → 3.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +2 -2
  4. data/.yardopts +0 -1
  5. data/Changelog.md +115 -35
  6. data/README.md +2 -2
  7. data/lib/rspec/expectations.rb +13 -8
  8. data/lib/rspec/{matchers → expectations}/configuration.rb +38 -13
  9. data/lib/rspec/expectations/expectation_target.rb +72 -8
  10. data/lib/rspec/expectations/fail_with.rb +10 -52
  11. data/lib/rspec/expectations/handler.rb +9 -11
  12. data/lib/rspec/expectations/syntax.rb +37 -35
  13. data/lib/rspec/expectations/version.rb +1 -1
  14. data/lib/rspec/matchers.rb +60 -9
  15. data/lib/rspec/matchers/aliased_matcher.rb +6 -0
  16. data/lib/rspec/matchers/built_in.rb +9 -1
  17. data/lib/rspec/matchers/built_in/all.rb +78 -0
  18. data/lib/rspec/matchers/built_in/base_matcher.rb +39 -1
  19. data/lib/rspec/matchers/built_in/be.rb +117 -42
  20. data/lib/rspec/matchers/built_in/be_between.rb +22 -0
  21. data/lib/rspec/matchers/built_in/be_instance_of.rb +11 -3
  22. data/lib/rspec/matchers/built_in/be_kind_of.rb +5 -0
  23. data/lib/rspec/matchers/built_in/be_within.rb +26 -6
  24. data/lib/rspec/matchers/built_in/change.rb +89 -13
  25. data/lib/rspec/matchers/built_in/compound.rb +19 -3
  26. data/lib/rspec/matchers/built_in/contain_exactly.rb +17 -6
  27. data/lib/rspec/matchers/built_in/cover.rb +3 -0
  28. data/lib/rspec/matchers/built_in/eq.rb +20 -5
  29. data/lib/rspec/matchers/built_in/eql.rb +15 -3
  30. data/lib/rspec/matchers/built_in/equal.rb +23 -6
  31. data/lib/rspec/matchers/built_in/exist.rb +74 -10
  32. data/lib/rspec/matchers/built_in/has.rb +58 -3
  33. data/lib/rspec/matchers/built_in/include.rb +16 -1
  34. data/lib/rspec/matchers/built_in/match.rb +14 -4
  35. data/lib/rspec/matchers/built_in/operators.rb +16 -0
  36. data/lib/rspec/matchers/built_in/output.rb +47 -5
  37. data/lib/rspec/matchers/built_in/raise_error.rb +40 -23
  38. data/lib/rspec/matchers/built_in/respond_to.rb +37 -16
  39. data/lib/rspec/matchers/built_in/satisfy.rb +15 -0
  40. data/lib/rspec/matchers/built_in/start_and_end_with.rb +29 -14
  41. data/lib/rspec/matchers/built_in/throw_symbol.rb +32 -3
  42. data/lib/rspec/matchers/built_in/yield.rb +148 -44
  43. data/lib/rspec/matchers/composable.rb +48 -7
  44. data/lib/rspec/matchers/dsl.rb +45 -17
  45. data/lib/rspec/matchers/generated_descriptions.rb +7 -0
  46. data/lib/rspec/matchers/matcher_delegator.rb +6 -2
  47. data/lib/rspec/matchers/pretty.rb +15 -19
  48. metadata +33 -236
  49. metadata.gz.sig +0 -0
  50. data/features/README.md +0 -48
  51. data/features/Upgrade.md +0 -53
  52. data/features/built_in_matchers/README.md +0 -96
  53. data/features/built_in_matchers/be.feature +0 -175
  54. data/features/built_in_matchers/be_within.feature +0 -48
  55. data/features/built_in_matchers/comparisons.feature +0 -97
  56. data/features/built_in_matchers/contain_exactly.feature +0 -46
  57. data/features/built_in_matchers/cover.feature +0 -47
  58. data/features/built_in_matchers/end_with.feature +0 -48
  59. data/features/built_in_matchers/equality.feature +0 -136
  60. data/features/built_in_matchers/exist.feature +0 -45
  61. data/features/built_in_matchers/expect_change.feature +0 -59
  62. data/features/built_in_matchers/expect_error.feature +0 -144
  63. data/features/built_in_matchers/include.feature +0 -126
  64. data/features/built_in_matchers/match.feature +0 -51
  65. data/features/built_in_matchers/output.feature +0 -70
  66. data/features/built_in_matchers/predicates.feature +0 -161
  67. data/features/built_in_matchers/respond_to.feature +0 -84
  68. data/features/built_in_matchers/satisfy.feature +0 -33
  69. data/features/built_in_matchers/start_with.feature +0 -48
  70. data/features/built_in_matchers/throw_symbol.feature +0 -91
  71. data/features/built_in_matchers/types.feature +0 -116
  72. data/features/built_in_matchers/yield.feature +0 -161
  73. data/features/composing_matchers.feature +0 -250
  74. data/features/compound_expectations.feature +0 -45
  75. data/features/custom_matchers/access_running_example.feature +0 -53
  76. data/features/custom_matchers/define_diffable_matcher.feature +0 -27
  77. data/features/custom_matchers/define_matcher.feature +0 -340
  78. data/features/custom_matchers/define_matcher_outside_rspec.feature +0 -34
  79. data/features/custom_matchers/define_matcher_with_fluent_interface.feature +0 -24
  80. data/features/customized_message.feature +0 -39
  81. data/features/diffing.feature +0 -85
  82. data/features/implicit_docstrings.feature +0 -52
  83. data/features/step_definitions/additional_cli_steps.rb +0 -22
  84. data/features/support/env.rb +0 -21
  85. data/features/support/rubinius.rb +0 -6
  86. data/features/syntax_configuration.feature +0 -71
  87. data/features/test_frameworks/minitest.feature +0 -44
  88. data/lib/rspec-expectations.rb +0 -1
  89. data/lib/rspec/expectations/diff_presenter.rb +0 -141
  90. data/lib/rspec/expectations/differ.rb +0 -44
  91. data/lib/rspec/expectations/encoded_string.rb +0 -56
  92. data/spec/rspec/expectations/diff_presenter_spec.rb +0 -249
  93. data/spec/rspec/expectations/encoded_string_spec.rb +0 -74
  94. data/spec/rspec/expectations/expectation_target_spec.rb +0 -82
  95. data/spec/rspec/expectations/extensions/kernel_spec.rb +0 -67
  96. data/spec/rspec/expectations/fail_with_spec.rb +0 -114
  97. data/spec/rspec/expectations/handler_spec.rb +0 -205
  98. data/spec/rspec/expectations/minitest_integration_spec.rb +0 -27
  99. data/spec/rspec/expectations/syntax_spec.rb +0 -89
  100. data/spec/rspec/expectations_spec.rb +0 -12
  101. data/spec/rspec/matchers/aliased_matcher_spec.rb +0 -48
  102. data/spec/rspec/matchers/aliases_spec.rb +0 -449
  103. data/spec/rspec/matchers/built_in/base_matcher_spec.rb +0 -83
  104. data/spec/rspec/matchers/built_in/be_between_spec.rb +0 -159
  105. data/spec/rspec/matchers/built_in/be_instance_of_spec.rb +0 -63
  106. data/spec/rspec/matchers/built_in/be_kind_of_spec.rb +0 -41
  107. data/spec/rspec/matchers/built_in/be_spec.rb +0 -592
  108. data/spec/rspec/matchers/built_in/be_within_spec.rb +0 -141
  109. data/spec/rspec/matchers/built_in/change_spec.rb +0 -808
  110. data/spec/rspec/matchers/built_in/compound_spec.rb +0 -292
  111. data/spec/rspec/matchers/built_in/contain_exactly_spec.rb +0 -441
  112. data/spec/rspec/matchers/built_in/cover_spec.rb +0 -69
  113. data/spec/rspec/matchers/built_in/eq_spec.rb +0 -156
  114. data/spec/rspec/matchers/built_in/eql_spec.rb +0 -41
  115. data/spec/rspec/matchers/built_in/equal_spec.rb +0 -106
  116. data/spec/rspec/matchers/built_in/exist_spec.rb +0 -124
  117. data/spec/rspec/matchers/built_in/has_spec.rb +0 -161
  118. data/spec/rspec/matchers/built_in/include_spec.rb +0 -540
  119. data/spec/rspec/matchers/built_in/match_spec.rb +0 -102
  120. data/spec/rspec/matchers/built_in/operators_spec.rb +0 -252
  121. data/spec/rspec/matchers/built_in/output_spec.rb +0 -165
  122. data/spec/rspec/matchers/built_in/raise_error_spec.rb +0 -461
  123. data/spec/rspec/matchers/built_in/respond_to_spec.rb +0 -292
  124. data/spec/rspec/matchers/built_in/satisfy_spec.rb +0 -44
  125. data/spec/rspec/matchers/built_in/start_and_end_with_spec.rb +0 -253
  126. data/spec/rspec/matchers/built_in/throw_symbol_spec.rb +0 -135
  127. data/spec/rspec/matchers/built_in/yield_spec.rb +0 -627
  128. data/spec/rspec/matchers/configuration_spec.rb +0 -213
  129. data/spec/rspec/matchers/description_generation_spec.rb +0 -191
  130. data/spec/rspec/matchers/dsl_spec.rb +0 -895
  131. data/spec/rspec/matchers/legacy_spec.rb +0 -101
  132. data/spec/rspec/matchers_spec.rb +0 -74
  133. data/spec/spec_helper.rb +0 -85
  134. data/spec/support/matchers.rb +0 -22
  135. data/spec/support/shared_examples.rb +0 -35
@@ -27,6 +27,12 @@ module RSpec
27
27
  AliasedMatcher.new(return_val, @description_block)
28
28
  end
29
29
 
30
+ # Provides the description of the aliased matcher. Aliased matchers
31
+ # are designed to behave identically to the original matcher except
32
+ # for this method. The description is different to reflect the aliased
33
+ # name.
34
+ #
35
+ # @api private
30
36
  def description
31
37
  @description_block.call(super)
32
38
  end
@@ -1,7 +1,14 @@
1
- require 'rspec/matchers/built_in/base_matcher'
1
+ RSpec::Support.require_rspec_matchers "built_in/base_matcher"
2
2
 
3
3
  module RSpec
4
4
  module Matchers
5
+ # Container module for all built-in matchers. The matcher classes are here
6
+ # (rather than directly under `RSpec::Matchers`) in order to prevent name
7
+ # collisions, since `RSpec::Matchers` gets included into the user's namespace.
8
+ #
9
+ # Autoloading is used to delay when the matcher classes get loaded, allowing
10
+ # rspec-matchers to boot faster, and avoiding loading matchers the user is
11
+ # not using.
5
12
  module BuiltIn
6
13
  autoload :BeAKindOf, 'rspec/matchers/built_in/be_kind_of'
7
14
  autoload :BeAnInstanceOf, 'rspec/matchers/built_in/be_instance_of'
@@ -24,6 +31,7 @@ module RSpec
24
31
  autoload :Exist, 'rspec/matchers/built_in/exist'
25
32
  autoload :Has, 'rspec/matchers/built_in/has'
26
33
  autoload :Include, 'rspec/matchers/built_in/include'
34
+ autoload :All, 'rspec/matchers/built_in/all'
27
35
  autoload :Match, 'rspec/matchers/built_in/match'
28
36
  autoload :NegativeOperatorMatcher, 'rspec/matchers/built_in/operators'
29
37
  autoload :OperatorMatcher, 'rspec/matchers/built_in/operators'
@@ -0,0 +1,78 @@
1
+ module RSpec
2
+ module Matchers
3
+ module BuiltIn
4
+ # @api private
5
+ # Provides the implementation for `all`.
6
+ # Not intended to be instantiated directly.
7
+ class All < BaseMatcher
8
+
9
+ # @private
10
+ attr_reader :matcher, :failed_objects
11
+
12
+ def initialize(matcher)
13
+ @matcher = matcher
14
+ @failed_objects = {}
15
+ end
16
+
17
+ # @private
18
+ def does_not_match?(actual)
19
+ raise NotImplementedError, '`expect().not_to all( matcher )` is not supported.'
20
+ end
21
+
22
+ # @api private
23
+ # @return [String]
24
+ def failure_message
25
+ all_messages = [improve_hash_formatting(super)]
26
+ failed_objects.each do |index, matcher_failure_message|
27
+ all_messages << failure_message_for_item(index, matcher_failure_message)
28
+ end
29
+ all_messages.join("\n\n")
30
+ end
31
+
32
+ # @api private
33
+ # @return [String]
34
+ def description
35
+ described_items = surface_descriptions_in(matcher)
36
+ improve_hash_formatting "all#{to_sentence(described_items)}"
37
+ end
38
+
39
+ private
40
+
41
+ def match(_, actual)
42
+ index_failed_objects
43
+ failed_objects.empty?
44
+ end
45
+
46
+ def index_failed_objects
47
+ actual.each_with_index do |actual_item, index|
48
+ cloned_matcher = matcher.clone
49
+ matches = cloned_matcher.matches?(actual_item)
50
+ failed_objects[index] = cloned_matcher.failure_message unless matches
51
+ end
52
+ end
53
+
54
+ def failure_message_for_item(index, failure_message)
55
+ failure_message = indent_multiline_message( add_new_line_if_needed(failure_message) )
56
+ indent_multiline_message("object at index #{index} failed to match:#{failure_message}")
57
+ end
58
+
59
+ def add_new_line_if_needed(message)
60
+ message.start_with?("\n") ? message : "\n#{message}"
61
+ end
62
+
63
+ def indent_multiline_message(message)
64
+ message = message.sub(/\n+\z/, '')
65
+ message.lines.map do |line|
66
+ line =~ /\S/ ? ' ' + line : line
67
+ end.join
68
+ end
69
+
70
+ def initialize_copy(other)
71
+ @matcher = @matcher.clone
72
+ super
73
+ end
74
+
75
+ end
76
+ end
77
+ end
78
+ end
@@ -4,7 +4,7 @@ module RSpec
4
4
  # @api private
5
5
  #
6
6
  # Used _internally_ as a base class for matchers that ship with
7
- # rspec-expectations.
7
+ # rspec-expectations and rspec-rails.
8
8
  #
9
9
  # ### Warning:
10
10
  #
@@ -15,19 +15,33 @@ module RSpec
15
15
  include RSpec::Matchers::Pretty
16
16
  include RSpec::Matchers::Composable
17
17
 
18
+ # @api private
19
+ # Used to detect when no arg is passed to `initialize`.
20
+ # `nil` cannot be used because it's a valid value to pass.
18
21
  UNDEFINED = Object.new.freeze
19
22
 
23
+ # @private
20
24
  attr_reader :actual, :expected, :rescued_exception
21
25
 
22
26
  def initialize(expected = UNDEFINED)
23
27
  @expected = expected unless UNDEFINED.equal?(expected)
24
28
  end
25
29
 
30
+ # @api private
31
+ # Indicates if the match is successful. Delegates to `match`, which
32
+ # should be defined on a subclass. Takes care of consistently
33
+ # initializing the `actual` attribute.
26
34
  def matches?(actual)
27
35
  @actual = actual
28
36
  match(expected, actual)
29
37
  end
30
38
 
39
+ # @api private
40
+ # Used to wrap a block of code that will indicate failure by
41
+ # raising one of the named exceptions.
42
+ #
43
+ # This is used by rspec-rails for some of its matchers that
44
+ # wrap rails' assertions.
31
45
  def match_unless_raises(*exceptions)
32
46
  exceptions.unshift Exception if exceptions.empty?
33
47
  begin
@@ -38,25 +52,49 @@ module RSpec
38
52
  end
39
53
  end
40
54
 
55
+ # @api private
56
+ # Provides a good generic failure message. Based on `description`.
57
+ # When subclassing, if you are not satisfied with this failure message
58
+ # you often only need to override `description`.
59
+ # @return [String]
41
60
  def failure_message
42
61
  assert_ivars :@actual
43
62
  "expected #{@actual.inspect} to #{description}"
44
63
  end
45
64
 
65
+ # @api private
66
+ # Provides a good generic negative failure message. Based on `description`.
67
+ # When subclassing, if you are not satisfied with this failure message
68
+ # you often only need to override `description`.
69
+ # @return [String]
46
70
  def failure_message_when_negated
47
71
  assert_ivars :@actual
48
72
  "expected #{@actual.inspect} not to #{description}"
49
73
  end
50
74
 
75
+ # @api private
76
+ # Generates a "pretty" description using the logic in {Pretty}.
77
+ # @return [String]
51
78
  def description
52
79
  return name_to_sentence unless defined?(@expected)
53
80
  "#{name_to_sentence}#{to_sentence @expected}"
54
81
  end
55
82
 
83
+ # @api private
84
+ # Matchers are not diffable by default. Override this to make your
85
+ # subclass diffable.
56
86
  def diffable?
57
87
  false
58
88
  end
59
89
 
90
+ # @api private
91
+ # Most matchers are value matchers (i.e. meant to work with `expect(value)`)
92
+ # rather than block matchers (i.e. meant to work with `expect { }`), so
93
+ # this defaults to false. Block matchers must override this to return true.
94
+ def supports_block_expectations?
95
+ false
96
+ end
97
+
60
98
  private
61
99
 
62
100
  def assert_ivars(*expected_ivars)
@@ -1,50 +1,79 @@
1
- require 'rspec/matchers/dsl'
2
-
3
1
  module RSpec
4
2
  module Matchers
5
3
  module BuiltIn
4
+ # @api private
5
+ # Provides the implementation for `be_truthy`.
6
+ # Not intended to be instantiated directly.
6
7
  class BeTruthy < BaseMatcher
7
- def match(_, actual)
8
- !!actual
9
- end
10
8
 
9
+ # @api private
10
+ # @return [String]
11
11
  def failure_message
12
12
  "expected: truthy value\n got: #{actual.inspect}"
13
13
  end
14
14
 
15
+ # @api private
16
+ # @return [String]
15
17
  def failure_message_when_negated
16
18
  "expected: falsey value\n got: #{actual.inspect}"
17
19
  end
18
- end
19
20
 
20
- class BeFalsey < BaseMatcher
21
+ private
22
+
21
23
  def match(_, actual)
22
- !actual
24
+ !!actual
23
25
  end
26
+ end
24
27
 
28
+ # @api private
29
+ # Provides the implementation for `be_falsey`.
30
+ # Not intended to be instantiated directly.
31
+ class BeFalsey < BaseMatcher
32
+
33
+ # @api private
34
+ # @return [String]
25
35
  def failure_message
26
36
  "expected: falsey value\n got: #{actual.inspect}"
27
37
  end
28
38
 
39
+ # @api private
40
+ # @return [String]
29
41
  def failure_message_when_negated
30
42
  "expected: truthy value\n got: #{actual.inspect}"
31
43
  end
32
- end
33
44
 
34
- class BeNil < BaseMatcher
45
+ private
46
+
35
47
  def match(_, actual)
36
- actual.nil?
48
+ !actual
37
49
  end
50
+ end
38
51
 
52
+ # @api private
53
+ # Provides the implementation for `be_nil`.
54
+ # Not intended to be instantiated directly.
55
+ class BeNil < BaseMatcher
56
+
57
+ # @api private
58
+ # @return [String]
39
59
  def failure_message
40
60
  "expected: nil\n got: #{actual.inspect}"
41
61
  end
42
62
 
63
+ # @api private
64
+ # @return [String]
43
65
  def failure_message_when_negated
44
66
  "expected: not nil\n got: nil"
45
67
  end
68
+
69
+ private
70
+
71
+ def match(_, actual)
72
+ actual.nil?
73
+ end
46
74
  end
47
75
 
76
+ # @private
48
77
  module BeHelpers
49
78
  private
50
79
 
@@ -69,6 +98,9 @@ module RSpec
69
98
  end
70
99
  end
71
100
 
101
+ # @api private
102
+ # Provides the implementation for `be`.
103
+ # Not intended to be instantiated directly.
72
104
  class Be < BaseMatcher
73
105
  include BeHelpers
74
106
 
@@ -76,14 +108,14 @@ module RSpec
76
108
  @args = args
77
109
  end
78
110
 
79
- def match(_, actual)
80
- !!actual
81
- end
82
-
111
+ # @api private
112
+ # @return [String]
83
113
  def failure_message
84
114
  "expected #{@actual.inspect} to evaluate to true"
85
115
  end
86
116
 
117
+ # @api private
118
+ # @return [String]
87
119
  def failure_message_when_negated
88
120
  "expected #{@actual.inspect} to evaluate to false"
89
121
  end
@@ -93,8 +125,17 @@ module RSpec
93
125
  BeComparedTo.new(operand, operator)
94
126
  end
95
127
  end
128
+
129
+ private
130
+
131
+ def match(_, actual)
132
+ !!actual
133
+ end
96
134
  end
97
135
 
136
+ # @api private
137
+ # Provides the implementation of `be <operator> value`.
138
+ # Not intended to be instantiated directly.
98
139
  class BeComparedTo < BaseMatcher
99
140
  include BeHelpers
100
141
 
@@ -108,10 +149,14 @@ module RSpec
108
149
  @actual.__send__ @operator, @expected
109
150
  end
110
151
 
152
+ # @api private
153
+ # @return [String]
111
154
  def failure_message
112
155
  "expected: #{@operator} #{@expected.inspect}\n got: #{@operator.to_s.gsub(/./, ' ')} #{@actual.inspect}"
113
156
  end
114
157
 
158
+ # @api private
159
+ # @return [String]
115
160
  def failure_message_when_negated
116
161
  message = "`expect(#{@actual.inspect}).not_to be #{@operator} #{@expected.inspect}`"
117
162
  if [:<, :>, :<=, :>=].include?(@operator)
@@ -121,11 +166,16 @@ module RSpec
121
166
  end
122
167
  end
123
168
 
169
+ # @api private
170
+ # @return [String]
124
171
  def description
125
172
  "be #{@operator} #{expected_to_sentence}#{args_to_sentence}"
126
173
  end
127
174
  end
128
175
 
176
+ # @api private
177
+ # Provides the implementation of `be_<predicate>`.
178
+ # Not intended to be instantiated directly.
129
179
  class BePredicate < BaseMatcher
130
180
  include BeHelpers
131
181
 
@@ -135,56 +185,68 @@ module RSpec
135
185
  @block = block
136
186
  end
137
187
 
138
- def matches?(actual)
139
- @actual = actual
140
-
141
- if is_private_on?( @actual )
142
- raise Expectations::ExpectationNotMetError.new("expectation set on private method `#{predicate}`")
143
- end
144
-
145
- begin
146
- return @result = actual.__send__(predicate, *@args, &@block)
147
- rescue NameError => predicate_missing_error
148
- end
188
+ def matches?(actual, &block)
189
+ @actual = actual
190
+ @block ||= block
191
+ predicate_accessible? && predicate_matches?
192
+ end
149
193
 
150
- begin
151
- return @result = actual.__send__(present_tense_predicate, *@args, &@block)
152
- rescue NameError
153
- raise predicate_missing_error
154
- end
194
+ def does_not_match?(actual, &block)
195
+ @actual = actual
196
+ @block ||= block
197
+ predicate_accessible? && !predicate_matches?
155
198
  end
156
199
 
200
+ # @api private
201
+ # @return [String]
157
202
  def failure_message
158
- "expected #{predicate}#{args_to_s} to return true, got #{@result.inspect}"
203
+ failure_message_expecting(true)
159
204
  end
160
205
 
206
+ # @api private
207
+ # @return [String]
161
208
  def failure_message_when_negated
162
- "expected #{predicate}#{args_to_s} to return false, got #{@result.inspect}"
209
+ failure_message_expecting(false)
163
210
  end
164
211
 
212
+ # @api private
213
+ # @return [String]
165
214
  def description
166
215
  "#{prefix_to_sentence}#{expected_to_sentence}#{args_to_sentence}"
167
216
  end
168
217
 
169
- private
218
+ private
219
+
220
+ def predicate_accessible?
221
+ !private_predicate? && predicate_exists?
222
+ end
170
223
 
171
- # support 1.8.7
172
- if methods.first.is_a? String
173
- def is_private_on? actual
174
- actual.private_methods.include? predicate.to_s
224
+ # support 1.8.7, evaluate once at load time for performance
225
+ if String === methods.first
226
+ def private_predicate?
227
+ @actual.private_methods.include? predicate.to_s
175
228
  end
176
229
  else
177
- def is_private_on? actual
178
- actual.private_methods.include? predicate
230
+ def private_predicate?
231
+ @actual.private_methods.include? predicate
179
232
  end
180
233
  end
181
234
 
235
+ def predicate_exists?
236
+ actual.respond_to?(predicate) || actual.respond_to?(present_tense_predicate)
237
+ end
238
+
239
+ def predicate_matches?
240
+ method_name = actual.respond_to?(predicate) ? predicate : present_tense_predicate
241
+ @predicate_matches = actual.__send__(method_name, *@args, &@block)
242
+ end
243
+
182
244
  def predicate
183
- "#{@expected}?".to_sym
245
+ :"#{@expected}?"
184
246
  end
185
247
 
186
248
  def present_tense_predicate
187
- "#{@expected}s?".to_sym
249
+ :"#{@expected}s?"
188
250
  end
189
251
 
190
252
  def parse_expected(expected)
@@ -199,6 +261,19 @@ module RSpec
199
261
  def prefix_to_sentence
200
262
  split_words(@prefix)
201
263
  end
264
+
265
+ def failure_message_expecting(value)
266
+ validity_message ||
267
+ "expected `#{@actual.inspect}.#{predicate}#{args_to_s}` to return #{value}, got #{@predicate_matches.inspect}"
268
+ end
269
+
270
+ def validity_message
271
+ if private_predicate?
272
+ "expected #{@actual} to respond to `#{predicate}` but `#{predicate}` is a private method"
273
+ elsif !predicate_exists?
274
+ "expected #{@actual} to respond to `#{predicate}`"
275
+ end
276
+ end
202
277
  end
203
278
  end
204
279
  end