opal-rspec-cj 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (176) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.gitmodules +15 -0
  4. data/.travis.yml +13 -0
  5. data/.yardopts +5 -0
  6. data/CHANGELOG.md +25 -0
  7. data/Gemfile +8 -0
  8. data/README.md +147 -0
  9. data/Rakefile +26 -0
  10. data/config.ru +10 -0
  11. data/example/Gemfile +4 -0
  12. data/example/README.md +13 -0
  13. data/example/Rakefile +8 -0
  14. data/example/opal/user.rb +11 -0
  15. data/example/spec/user_spec.rb +15 -0
  16. data/lib/opal-rspec.rb +2 -0
  17. data/lib/opal/rspec.rb +20 -0
  18. data/lib/opal/rspec/rake_task.rb +63 -0
  19. data/lib/opal/rspec/version.rb +5 -0
  20. data/opal-rspec.gemspec +21 -0
  21. data/opal/opal-rspec.rb +1 -0
  22. data/opal/opal/rspec.rb +25 -0
  23. data/opal/opal/rspec/async.rb +289 -0
  24. data/opal/opal/rspec/browser_formatter.rb +188 -0
  25. data/opal/opal/rspec/fixes.rb +116 -0
  26. data/opal/opal/rspec/requires.rb +45 -0
  27. data/opal/opal/rspec/runner.rb +69 -0
  28. data/opal/opal/rspec/sprockets_runner.rb.erb +11 -0
  29. data/opal/opal/rspec/text_formatter.rb +74 -0
  30. data/spec/async_spec.rb +38 -0
  31. data/spec/example_spec.rb +163 -0
  32. data/spec/matchers_spec.rb +201 -0
  33. data/spec/mock_spec.rb +63 -0
  34. data/spec/named_subject_spec.rb +11 -0
  35. data/spec/should_syntax_spec.rb +17 -0
  36. data/vendor/spec_runner.js +50 -0
  37. data/vendor_lib/rspec-expectations.rb +1 -0
  38. data/vendor_lib/rspec.rb +3 -0
  39. data/vendor_lib/rspec/autorun.rb +2 -0
  40. data/vendor_lib/rspec/core.rb +203 -0
  41. data/vendor_lib/rspec/core/backport_random.rb +302 -0
  42. data/vendor_lib/rspec/core/backtrace_formatter.rb +65 -0
  43. data/vendor_lib/rspec/core/command_line.rb +36 -0
  44. data/vendor_lib/rspec/core/configuration.rb +1129 -0
  45. data/vendor_lib/rspec/core/configuration_options.rb +143 -0
  46. data/vendor_lib/rspec/core/drb_command_line.rb +26 -0
  47. data/vendor_lib/rspec/core/drb_options.rb +87 -0
  48. data/vendor_lib/rspec/core/dsl.rb +26 -0
  49. data/vendor_lib/rspec/core/example.rb +312 -0
  50. data/vendor_lib/rspec/core/example_group.rb +540 -0
  51. data/vendor_lib/rspec/core/filter_manager.rb +224 -0
  52. data/vendor_lib/rspec/core/flat_map.rb +17 -0
  53. data/vendor_lib/rspec/core/formatters.rb +54 -0
  54. data/vendor_lib/rspec/core/formatters/base_formatter.rb +291 -0
  55. data/vendor_lib/rspec/core/formatters/base_text_formatter.rb +307 -0
  56. data/vendor_lib/rspec/core/formatters/deprecation_formatter.rb +193 -0
  57. data/vendor_lib/rspec/core/formatters/documentation_formatter.rb +67 -0
  58. data/vendor_lib/rspec/core/formatters/helpers.rb +82 -0
  59. data/vendor_lib/rspec/core/formatters/html_formatter.rb +155 -0
  60. data/vendor_lib/rspec/core/formatters/html_printer.rb +408 -0
  61. data/vendor_lib/rspec/core/formatters/json_formatter.rb +99 -0
  62. data/vendor_lib/rspec/core/formatters/progress_formatter.rb +32 -0
  63. data/vendor_lib/rspec/core/formatters/snippet_extractor.rb +101 -0
  64. data/vendor_lib/rspec/core/hooks.rb +535 -0
  65. data/vendor_lib/rspec/core/memoized_helpers.rb +431 -0
  66. data/vendor_lib/rspec/core/metadata.rb +313 -0
  67. data/vendor_lib/rspec/core/mocking/with_absolutely_nothing.rb +11 -0
  68. data/vendor_lib/rspec/core/mocking/with_flexmock.rb +27 -0
  69. data/vendor_lib/rspec/core/mocking/with_mocha.rb +52 -0
  70. data/vendor_lib/rspec/core/mocking/with_rr.rb +27 -0
  71. data/vendor_lib/rspec/core/mocking/with_rspec.rb +27 -0
  72. data/vendor_lib/rspec/core/option_parser.rb +234 -0
  73. data/vendor_lib/rspec/core/ordering.rb +154 -0
  74. data/vendor_lib/rspec/core/pending.rb +110 -0
  75. data/vendor_lib/rspec/core/project_initializer.rb +88 -0
  76. data/vendor_lib/rspec/core/rake_task.rb +128 -0
  77. data/vendor_lib/rspec/core/reporter.rb +132 -0
  78. data/vendor_lib/rspec/core/ruby_project.rb +44 -0
  79. data/vendor_lib/rspec/core/runner.rb +97 -0
  80. data/vendor_lib/rspec/core/shared_context.rb +53 -0
  81. data/vendor_lib/rspec/core/shared_example_group.rb +146 -0
  82. data/vendor_lib/rspec/core/shared_example_group/collection.rb +27 -0
  83. data/vendor_lib/rspec/core/version.rb +7 -0
  84. data/vendor_lib/rspec/core/warnings.rb +22 -0
  85. data/vendor_lib/rspec/core/world.rb +131 -0
  86. data/vendor_lib/rspec/expectations.rb +75 -0
  87. data/vendor_lib/rspec/expectations/differ.rb +154 -0
  88. data/vendor_lib/rspec/expectations/errors.rb +9 -0
  89. data/vendor_lib/rspec/expectations/expectation_target.rb +87 -0
  90. data/vendor_lib/rspec/expectations/extensions.rb +1 -0
  91. data/vendor_lib/rspec/expectations/extensions/object.rb +29 -0
  92. data/vendor_lib/rspec/expectations/fail_with.rb +79 -0
  93. data/vendor_lib/rspec/expectations/handler.rb +68 -0
  94. data/vendor_lib/rspec/expectations/syntax.rb +182 -0
  95. data/vendor_lib/rspec/expectations/version.rb +8 -0
  96. data/vendor_lib/rspec/matchers.rb +633 -0
  97. data/vendor_lib/rspec/matchers/built_in.rb +39 -0
  98. data/vendor_lib/rspec/matchers/built_in/base_matcher.rb +68 -0
  99. data/vendor_lib/rspec/matchers/built_in/be.rb +213 -0
  100. data/vendor_lib/rspec/matchers/built_in/be_instance_of.rb +15 -0
  101. data/vendor_lib/rspec/matchers/built_in/be_kind_of.rb +11 -0
  102. data/vendor_lib/rspec/matchers/built_in/be_within.rb +55 -0
  103. data/vendor_lib/rspec/matchers/built_in/change.rb +141 -0
  104. data/vendor_lib/rspec/matchers/built_in/cover.rb +21 -0
  105. data/vendor_lib/rspec/matchers/built_in/eq.rb +22 -0
  106. data/vendor_lib/rspec/matchers/built_in/eql.rb +23 -0
  107. data/vendor_lib/rspec/matchers/built_in/equal.rb +48 -0
  108. data/vendor_lib/rspec/matchers/built_in/exist.rb +26 -0
  109. data/vendor_lib/rspec/matchers/built_in/has.rb +48 -0
  110. data/vendor_lib/rspec/matchers/built_in/include.rb +61 -0
  111. data/vendor_lib/rspec/matchers/built_in/match.rb +17 -0
  112. data/vendor_lib/rspec/matchers/built_in/match_array.rb +51 -0
  113. data/vendor_lib/rspec/matchers/built_in/raise_error.rb +154 -0
  114. data/vendor_lib/rspec/matchers/built_in/respond_to.rb +74 -0
  115. data/vendor_lib/rspec/matchers/built_in/satisfy.rb +30 -0
  116. data/vendor_lib/rspec/matchers/built_in/start_and_end_with.rb +48 -0
  117. data/vendor_lib/rspec/matchers/built_in/throw_symbol.rb +94 -0
  118. data/vendor_lib/rspec/matchers/built_in/yield.rb +297 -0
  119. data/vendor_lib/rspec/matchers/compatibility.rb +14 -0
  120. data/vendor_lib/rspec/matchers/configuration.rb +113 -0
  121. data/vendor_lib/rspec/matchers/dsl.rb +23 -0
  122. data/vendor_lib/rspec/matchers/generated_descriptions.rb +35 -0
  123. data/vendor_lib/rspec/matchers/matcher.rb +301 -0
  124. data/vendor_lib/rspec/matchers/method_missing.rb +12 -0
  125. data/vendor_lib/rspec/matchers/operator_matcher.rb +99 -0
  126. data/vendor_lib/rspec/matchers/pretty.rb +70 -0
  127. data/vendor_lib/rspec/matchers/test_unit_integration.rb +11 -0
  128. data/vendor_lib/rspec/mocks.rb +100 -0
  129. data/vendor_lib/rspec/mocks/any_instance/chain.rb +92 -0
  130. data/vendor_lib/rspec/mocks/any_instance/expectation_chain.rb +47 -0
  131. data/vendor_lib/rspec/mocks/any_instance/message_chains.rb +75 -0
  132. data/vendor_lib/rspec/mocks/any_instance/recorder.rb +200 -0
  133. data/vendor_lib/rspec/mocks/any_instance/stub_chain.rb +45 -0
  134. data/vendor_lib/rspec/mocks/any_instance/stub_chain_chain.rb +23 -0
  135. data/vendor_lib/rspec/mocks/argument_list_matcher.rb +104 -0
  136. data/vendor_lib/rspec/mocks/argument_matchers.rb +264 -0
  137. data/vendor_lib/rspec/mocks/arity_calculator.rb +66 -0
  138. data/vendor_lib/rspec/mocks/configuration.rb +111 -0
  139. data/vendor_lib/rspec/mocks/error_generator.rb +203 -0
  140. data/vendor_lib/rspec/mocks/errors.rb +12 -0
  141. data/vendor_lib/rspec/mocks/example_methods.rb +201 -0
  142. data/vendor_lib/rspec/mocks/extensions/marshal.rb +17 -0
  143. data/vendor_lib/rspec/mocks/framework.rb +36 -0
  144. data/vendor_lib/rspec/mocks/instance_method_stasher.rb +112 -0
  145. data/vendor_lib/rspec/mocks/matchers/have_received.rb +99 -0
  146. data/vendor_lib/rspec/mocks/matchers/receive.rb +112 -0
  147. data/vendor_lib/rspec/mocks/matchers/receive_messages.rb +72 -0
  148. data/vendor_lib/rspec/mocks/message_expectation.rb +643 -0
  149. data/vendor_lib/rspec/mocks/method_double.rb +209 -0
  150. data/vendor_lib/rspec/mocks/method_reference.rb +95 -0
  151. data/vendor_lib/rspec/mocks/mock.rb +7 -0
  152. data/vendor_lib/rspec/mocks/mutate_const.rb +406 -0
  153. data/vendor_lib/rspec/mocks/object_reference.rb +90 -0
  154. data/vendor_lib/rspec/mocks/order_group.rb +82 -0
  155. data/vendor_lib/rspec/mocks/proxy.rb +269 -0
  156. data/vendor_lib/rspec/mocks/proxy_for_nil.rb +37 -0
  157. data/vendor_lib/rspec/mocks/space.rb +95 -0
  158. data/vendor_lib/rspec/mocks/standalone.rb +3 -0
  159. data/vendor_lib/rspec/mocks/stub_chain.rb +51 -0
  160. data/vendor_lib/rspec/mocks/syntax.rb +374 -0
  161. data/vendor_lib/rspec/mocks/targets.rb +90 -0
  162. data/vendor_lib/rspec/mocks/test_double.rb +109 -0
  163. data/vendor_lib/rspec/mocks/verifying_double.rb +77 -0
  164. data/vendor_lib/rspec/mocks/verifying_message_expecation.rb +60 -0
  165. data/vendor_lib/rspec/mocks/verifying_proxy.rb +151 -0
  166. data/vendor_lib/rspec/mocks/version.rb +7 -0
  167. data/vendor_lib/rspec/support.rb +6 -0
  168. data/vendor_lib/rspec/support/caller_filter.rb +56 -0
  169. data/vendor_lib/rspec/support/spec.rb +14 -0
  170. data/vendor_lib/rspec/support/spec/deprecation_helpers.rb +29 -0
  171. data/vendor_lib/rspec/support/spec/in_sub_process.rb +40 -0
  172. data/vendor_lib/rspec/support/spec/stderr_splitter.rb +50 -0
  173. data/vendor_lib/rspec/support/version.rb +7 -0
  174. data/vendor_lib/rspec/support/warnings.rb +41 -0
  175. data/vendor_lib/rspec/version.rb +5 -0
  176. metadata +268 -0
@@ -0,0 +1,643 @@
1
+ module RSpec
2
+ module Mocks
3
+
4
+ # A message expectation that only allows concrete return values to be set
5
+ # for a message. While this same effect can be achieved using a standard
6
+ # MessageExpecation, this version is much faster and so can be used as an
7
+ # optimization.
8
+ class SimpleMessageExpectation
9
+
10
+ def initialize(message, response, error_generator, backtrace_line = nil)
11
+ @message, @response, @error_generator, @backtrace_line = message, response, error_generator, backtrace_line
12
+ @received = false
13
+ end
14
+
15
+ def invoke(*_)
16
+ @received = true
17
+ @response
18
+ end
19
+
20
+ def matches?(message, *_)
21
+ @message == message
22
+ end
23
+
24
+ def called_max_times?
25
+ false
26
+ end
27
+
28
+ def verify_messages_received
29
+ InsertOntoBacktrace.line(@backtrace_line) do
30
+ unless @received
31
+ @error_generator.raise_expectation_error(@message, 1, ArgumentListMatcher::MATCH_ALL, 0, nil)
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ class MessageExpectation
38
+ # @private
39
+ attr_accessor :error_generator, :implementation
40
+ attr_reader :message
41
+ attr_reader :orig_object
42
+ attr_writer :expected_received_count, :expected_from, :argument_list_matcher
43
+ protected :expected_received_count=, :expected_from=, :error_generator, :error_generator=, :implementation=
44
+
45
+ # @private
46
+ def initialize(error_generator, expectation_ordering, expected_from, method_double,
47
+ expected_received_count=1, opts={}, &implementation_block)
48
+ @error_generator = error_generator
49
+ @error_generator.opts = opts
50
+ @expected_from = expected_from
51
+ @method_double = method_double
52
+ @orig_object = @method_double.object
53
+ @message = @method_double.method_name
54
+ @actual_received_count = 0
55
+ @expected_received_count = expected_received_count
56
+ @argument_list_matcher = ArgumentListMatcher::MATCH_ALL
57
+ @order_group = expectation_ordering
58
+ @order_group.register(self)
59
+ @ordered = false
60
+ @at_least = @at_most = @exactly = nil
61
+ @args_to_yield = []
62
+ @failed_fast = nil
63
+ @eval_context = nil
64
+ @yield_receiver_to_implementation_block = false
65
+
66
+ @implementation = Implementation.new
67
+ self.inner_implementation_action = implementation_block
68
+ end
69
+
70
+ # @private
71
+
72
+ # @private
73
+ def expected_args
74
+ @argument_list_matcher.expected_args
75
+ end
76
+
77
+ # @overload and_return(value)
78
+ # @overload and_return(first_value, second_value)
79
+ # @overload and_return(&block)
80
+ #
81
+ # Tells the object to return a value when it receives the message. Given
82
+ # more than one value, the first value is returned the first time the
83
+ # message is received, the second value is returned the next time, etc,
84
+ # etc.
85
+ #
86
+ # If the message is received more times than there are values, the last
87
+ # value is received for every subsequent call.
88
+ #
89
+ # The block format is still supported, but is unofficially deprecated in
90
+ # favor of just passing a block to the stub method.
91
+ #
92
+ # @example
93
+ #
94
+ # counter.stub(:count).and_return(1)
95
+ # counter.count # => 1
96
+ # counter.count # => 1
97
+ #
98
+ # counter.stub(:count).and_return(1,2,3)
99
+ # counter.count # => 1
100
+ # counter.count # => 2
101
+ # counter.count # => 3
102
+ # counter.count # => 3
103
+ # counter.count # => 3
104
+ # # etc
105
+ #
106
+ # # Supported, but ...
107
+ # counter.stub(:count).and_return { 1 }
108
+ # counter.count # => 1
109
+ #
110
+ # # ... this is prefered
111
+ # counter.stub(:count) { 1 }
112
+ # counter.count # => 1
113
+ def and_return(*values, &implementation)
114
+ if negative?
115
+ raise "`and_return` is not supported with negative message expectations"
116
+ else
117
+ @expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0 and @at_least)
118
+
119
+ if implementation
120
+ # TODO: deprecate `and_return { value }`
121
+ self.inner_implementation_action = implementation
122
+ else
123
+ self.terminal_implementation_action = AndReturnImplementation.new(values)
124
+ end
125
+
126
+ nil
127
+ end
128
+ end
129
+
130
+ def and_yield_receiver_to_implementation
131
+ @yield_receiver_to_implementation_block = true
132
+ self
133
+ end
134
+
135
+ def yield_receiver_to_implementation_block?
136
+ @yield_receiver_to_implementation_block
137
+ end
138
+
139
+ # Tells the object to delegate to the original unmodified method
140
+ # when it receives the message.
141
+ #
142
+ # @note This is only available on partial mock objects.
143
+ #
144
+ # @example
145
+ #
146
+ # counter.should_receive(:increment).and_call_original
147
+ # original_count = counter.count
148
+ # counter.increment
149
+ # expect(counter.count).to eq(original_count + 1)
150
+ def and_call_original
151
+ if RSpec::Mocks::TestDouble === @method_double.object
152
+ @error_generator.raise_only_valid_on_a_partial_mock(:and_call_original)
153
+ else
154
+ if implementation.inner_action
155
+ RSpec.warning("You're overriding a previous implementation for this stub")
156
+ end
157
+ @implementation = AndCallOriginalImplementation.new(@method_double.original_method)
158
+ @yield_receiver_to_implementation_block = false
159
+ end
160
+ end
161
+
162
+ # @overload and_raise
163
+ # @overload and_raise(ExceptionClass)
164
+ # @overload and_raise(ExceptionClass, message)
165
+ # @overload and_raise(exception_instance)
166
+ #
167
+ # Tells the object to raise an exception when the message is received.
168
+ #
169
+ # @note
170
+ #
171
+ # When you pass an exception class, the MessageExpectation will raise
172
+ # an instance of it, creating it with `exception` and passing `message`
173
+ # if specified. If the exception class initializer requires more than
174
+ # one parameters, you must pass in an instance and not the class,
175
+ # otherwise this method will raise an ArgumentError exception.
176
+ #
177
+ # @example
178
+ #
179
+ # car.stub(:go).and_raise
180
+ # car.stub(:go).and_raise(OutOfGas)
181
+ # car.stub(:go).and_raise(OutOfGas, "At least 2 oz of gas needed to drive")
182
+ # car.stub(:go).and_raise(OutOfGas.new(2, :oz))
183
+ def and_raise(exception = RuntimeError, message = nil)
184
+ if exception.respond_to?(:exception)
185
+ exception = message ? exception.exception(message) : exception.exception
186
+ end
187
+
188
+ self.terminal_implementation_action = Proc.new { raise exception }
189
+ nil
190
+ end
191
+
192
+ # @overload and_throw(symbol)
193
+ # @overload and_throw(symbol, object)
194
+ #
195
+ # Tells the object to throw a symbol (with the object if that form is
196
+ # used) when the message is received.
197
+ #
198
+ # @example
199
+ #
200
+ # car.stub(:go).and_throw(:out_of_gas)
201
+ # car.stub(:go).and_throw(:out_of_gas, :level => 0.1)
202
+ def and_throw(*args)
203
+ self.terminal_implementation_action = Proc.new { throw(*args) }
204
+ nil
205
+ end
206
+
207
+ # Tells the object to yield one or more args to a block when the message
208
+ # is received.
209
+ #
210
+ # @example
211
+ #
212
+ # stream.stub(:open).and_yield(StringIO.new)
213
+ def and_yield(*args, &block)
214
+ yield @eval_context = Object.new if block
215
+ @args_to_yield << args
216
+ self.initial_implementation_action = AndYieldImplementation.new(@args_to_yield, @eval_context, @error_generator)
217
+ self
218
+ end
219
+
220
+ # @private
221
+ def matches?(message, *args)
222
+ @message == message && @argument_list_matcher.args_match?(*args)
223
+ end
224
+
225
+ # @private
226
+ def invoke(parent_stub, *args, &block)
227
+ if yield_receiver_to_implementation_block?
228
+ args.unshift(orig_object)
229
+ end
230
+
231
+ if negative? || ((@exactly || @at_most) && (@actual_received_count == @expected_received_count))
232
+ @actual_received_count += 1
233
+ @failed_fast = true
234
+ #args are the args we actually received, @argument_list_matcher is the
235
+ #list of args we were expecting
236
+ @error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *args)
237
+ end
238
+
239
+ @order_group.handle_order_constraint self
240
+
241
+ begin
242
+ if implementation.present?
243
+ implementation.call(*args, &block)
244
+ elsif parent_stub
245
+ parent_stub.invoke(nil, *args, &block)
246
+ end
247
+ ensure
248
+ @actual_received_count += 1
249
+ end
250
+ end
251
+
252
+ # @private
253
+ def negative?
254
+ @expected_received_count == 0 && !@at_least
255
+ end
256
+
257
+ # @private
258
+ def called_max_times?
259
+ @expected_received_count != :any &&
260
+ !@at_least &&
261
+ @expected_received_count > 0 &&
262
+ @actual_received_count >= @expected_received_count
263
+ end
264
+
265
+ # @private
266
+ def matches_name_but_not_args(message, *args)
267
+ @message == message and not @argument_list_matcher.args_match?(*args)
268
+ end
269
+
270
+ # @private
271
+ def verify_messages_received
272
+ InsertOntoBacktrace.line(@expected_from) do
273
+ generate_error unless expected_messages_received? || failed_fast?
274
+ end
275
+ end
276
+
277
+ # @private
278
+ def expected_messages_received?
279
+ ignoring_args? || matches_exact_count? || matches_at_least_count? || matches_at_most_count?
280
+ end
281
+
282
+ def ensure_expected_ordering_received!
283
+ @order_group.verify_invocation_order(self) if @ordered
284
+ true
285
+ end
286
+
287
+ # @private
288
+ def ignoring_args?
289
+ @expected_received_count == :any
290
+ end
291
+
292
+ # @private
293
+ def matches_at_least_count?
294
+ @at_least && @actual_received_count >= @expected_received_count
295
+ end
296
+
297
+ # @private
298
+ def matches_at_most_count?
299
+ @at_most && @actual_received_count <= @expected_received_count
300
+ end
301
+
302
+ # @private
303
+ def matches_exact_count?
304
+ @expected_received_count == @actual_received_count
305
+ end
306
+
307
+ # @private
308
+ def similar_messages
309
+ @similar_messages ||= []
310
+ end
311
+
312
+ # @private
313
+ def advise(*args)
314
+ similar_messages << args
315
+ end
316
+
317
+ # @private
318
+ def generate_error
319
+ if similar_messages.empty?
320
+ @error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *expected_args)
321
+ else
322
+ @error_generator.raise_similar_message_args_error(self, *@similar_messages)
323
+ end
324
+ end
325
+
326
+ def expectation_count_type
327
+ return :at_least if @at_least
328
+ return :at_most if @at_most
329
+ return nil
330
+ end
331
+
332
+ # @private
333
+ def description
334
+ @error_generator.describe_expectation(@message, @expected_received_count, @actual_received_count, *expected_args)
335
+ end
336
+
337
+ def raise_out_of_order_error
338
+ @error_generator.raise_out_of_order_error @message
339
+ end
340
+
341
+ # Constrains a stub or message expectation to invocations with specific
342
+ # arguments.
343
+ #
344
+ # With a stub, if the message might be received with other args as well,
345
+ # you should stub a default value first, and then stub or mock the same
346
+ # message using `with` to constrain to specific arguments.
347
+ #
348
+ # A message expectation will fail if the message is received with different
349
+ # arguments.
350
+ #
351
+ # @example
352
+ #
353
+ # cart.stub(:add) { :failure }
354
+ # cart.stub(:add).with(Book.new(:isbn => 1934356379)) { :success }
355
+ # cart.add(Book.new(:isbn => 1234567890))
356
+ # # => :failure
357
+ # cart.add(Book.new(:isbn => 1934356379))
358
+ # # => :success
359
+ #
360
+ # cart.should_receive(:add).with(Book.new(:isbn => 1934356379)) { :success }
361
+ # cart.add(Book.new(:isbn => 1234567890))
362
+ # # => failed expectation
363
+ # cart.add(Book.new(:isbn => 1934356379))
364
+ # # => passes
365
+ def with(*args, &block)
366
+ if args.empty?
367
+ raise ArgumentError,
368
+ "`with` must have at least one argument. Use `no_args` matcher to set the expectation of receiving no arguments."
369
+ end
370
+
371
+ self.inner_implementation_action = block
372
+ @argument_list_matcher = ArgumentListMatcher.new(*args)
373
+ self
374
+ end
375
+
376
+ # Constrain a message expectation to be received a specific number of
377
+ # times.
378
+ #
379
+ # @example
380
+ #
381
+ # dealer.should_receive(:deal_card).exactly(10).times
382
+ def exactly(n, &block)
383
+ self.inner_implementation_action = block
384
+ set_expected_received_count :exactly, n
385
+ self
386
+ end
387
+
388
+ # Constrain a message expectation to be received at least a specific
389
+ # number of times.
390
+ #
391
+ # @example
392
+ #
393
+ # dealer.should_receive(:deal_card).at_least(9).times
394
+ def at_least(n, &block)
395
+ set_expected_received_count :at_least, n
396
+
397
+ if n == 0
398
+ raise "at_least(0) has been removed, use allow(...).to receive(:message) instead"
399
+ end
400
+
401
+ self.inner_implementation_action = block
402
+
403
+ self
404
+ end
405
+
406
+ # Constrain a message expectation to be received at most a specific
407
+ # number of times.
408
+ #
409
+ # @example
410
+ #
411
+ # dealer.should_receive(:deal_card).at_most(10).times
412
+ def at_most(n, &block)
413
+ self.inner_implementation_action = block
414
+ set_expected_received_count :at_most, n
415
+ self
416
+ end
417
+
418
+ # Syntactic sugar for `exactly`, `at_least` and `at_most`
419
+ #
420
+ # @example
421
+ #
422
+ # dealer.should_receive(:deal_card).exactly(10).times
423
+ # dealer.should_receive(:deal_card).at_least(10).times
424
+ # dealer.should_receive(:deal_card).at_most(10).times
425
+ def times(&block)
426
+ self.inner_implementation_action = block
427
+ self
428
+ end
429
+
430
+ # Expect a message not to be received at all.
431
+ #
432
+ # @example
433
+ #
434
+ # car.should_receive(:stop).never
435
+ def never
436
+ ErrorGenerator.raise_double_negation_error("expect(obj)") if negative?
437
+ @expected_received_count = 0
438
+ self
439
+ end
440
+
441
+ # Expect a message to be received exactly one time.
442
+ #
443
+ # @example
444
+ #
445
+ # car.should_receive(:go).once
446
+ def once(&block)
447
+ self.inner_implementation_action = block
448
+ set_expected_received_count :exactly, 1
449
+ self
450
+ end
451
+
452
+ # Expect a message to be received exactly two times.
453
+ #
454
+ # @example
455
+ #
456
+ # car.should_receive(:go).twice
457
+ def twice(&block)
458
+ self.inner_implementation_action = block
459
+ set_expected_received_count :exactly, 2
460
+ self
461
+ end
462
+
463
+ # Expect messages to be received in a specific order.
464
+ #
465
+ # @example
466
+ #
467
+ # api.should_receive(:prepare).ordered
468
+ # api.should_receive(:run).ordered
469
+ # api.should_receive(:finish).ordered
470
+ def ordered(&block)
471
+ self.inner_implementation_action = block
472
+ @ordered = true
473
+ self
474
+ end
475
+
476
+ # @private
477
+ def ordered?
478
+ @ordered
479
+ end
480
+
481
+ # @private
482
+ def negative_expectation_for?(message)
483
+ @message == message && negative?
484
+ end
485
+
486
+ # @private
487
+ def actual_received_count_matters?
488
+ @at_least || @at_most || @exactly
489
+ end
490
+
491
+ # @private
492
+ def increase_actual_received_count!
493
+ @actual_received_count += 1
494
+ end
495
+
496
+ private
497
+
498
+ def failed_fast?
499
+ @failed_fast
500
+ end
501
+
502
+ def set_expected_received_count(relativity, n)
503
+ @at_least = (relativity == :at_least)
504
+ @at_most = (relativity == :at_most)
505
+ @exactly = (relativity == :exactly)
506
+ @expected_received_count = case n
507
+ when Numeric then n
508
+ when :once then 1
509
+ when :twice then 2
510
+ end
511
+ end
512
+
513
+ def initial_implementation_action=(action)
514
+ implementation.initial_action = action
515
+ end
516
+
517
+ def inner_implementation_action=(action)
518
+ RSpec.warning("You're overriding a previous implementation for this stub") if implementation.inner_action
519
+ implementation.inner_action = action if action
520
+ end
521
+
522
+ def terminal_implementation_action=(action)
523
+ implementation.terminal_action = action
524
+ end
525
+ end
526
+
527
+ # Handles the implementation of an `and_yield` declaration.
528
+ # @private
529
+ class AndYieldImplementation
530
+ def initialize(args_to_yield, eval_context, error_generator)
531
+ @args_to_yield = args_to_yield
532
+ @eval_context = eval_context
533
+ @error_generator = error_generator
534
+ end
535
+
536
+ def call(*args_to_ignore, &block)
537
+ return if @args_to_yield.empty? && @eval_context.nil?
538
+
539
+ @error_generator.raise_missing_block_error @args_to_yield unless block
540
+ value = nil
541
+ @args_to_yield.each do |args|
542
+ if block.arity > -1 && args.length != block.arity
543
+ @error_generator.raise_wrong_arity_error args, block.arity
544
+ end
545
+ value = @eval_context ? @eval_context.instance_exec(*args, &block) : block.call(*args)
546
+ end
547
+ value
548
+ end
549
+ end
550
+
551
+ # Handles the implementation of an `and_return` implementation.
552
+ # @private
553
+ class AndReturnImplementation
554
+ def initialize(values_to_return)
555
+ @values_to_return = values_to_return
556
+ end
557
+
558
+ def call(*args_to_ignore, &block)
559
+ if @values_to_return.size > 1
560
+ @values_to_return.shift
561
+ else
562
+ @values_to_return.first
563
+ end
564
+ end
565
+ end
566
+
567
+ # Represents a configured implementation. Takes into account
568
+ # any number of sub-implementations.
569
+ # @private
570
+ class Implementation
571
+ attr_accessor :initial_action, :inner_action, :terminal_action
572
+
573
+ def call(*args, &block)
574
+ actions.map do |action|
575
+ action.call(*args, &block)
576
+ end.last
577
+ end
578
+
579
+ def present?
580
+ actions.any?
581
+ end
582
+
583
+ private
584
+
585
+ def actions
586
+ [initial_action, inner_action, terminal_action].compact
587
+ end
588
+ end
589
+
590
+ # Represents an `and_call_original` implementation.
591
+ # @private
592
+ class AndCallOriginalImplementation
593
+ def initialize(method)
594
+ @method = method
595
+ end
596
+
597
+ CannotModifyFurtherError = Class.new(StandardError)
598
+
599
+ def initial_action=(value)
600
+ raise cannot_modify_further_error
601
+ end
602
+
603
+ def inner_action=(value)
604
+ raise cannot_modify_further_error
605
+ end
606
+
607
+ def terminal_action=(value)
608
+ raise cannot_modify_further_error
609
+ end
610
+
611
+ def present?
612
+ true
613
+ end
614
+
615
+ def inner_action
616
+ true
617
+ end
618
+
619
+ def call(*args, &block)
620
+ @method.call(*args, &block)
621
+ end
622
+
623
+ private
624
+
625
+ def cannot_modify_further_error
626
+ CannotModifyFurtherError.new "This method has already been configured " +
627
+ "to call the original implementation, and cannot be modified further."
628
+ end
629
+ end
630
+
631
+ # Insert original locations into stacktraces
632
+ # @api private
633
+ class InsertOntoBacktrace
634
+ def self.line(location)
635
+ yield
636
+ rescue RSpec::Mocks::MockExpectationError => error
637
+ error.backtrace.insert(0, location)
638
+ Kernel::raise error
639
+ end
640
+ end
641
+
642
+ end
643
+ end