Class: RSpec::Mocks::MessageExpectation

Inherits:
Object
  • Object
show all
Defined in:
lib/rspec/mocks/message_expectation.rb

Overview

Represents an individual method stub or message expectation. The methods defined here can be used to configure how it behaves. The methods return self so that they can be chained together to form a fluent interface.

Direct Known Subclasses

VerifyingMessageExpectation

Configuring Responses collapse

Constraining Receive Counts collapse

Other Constraints collapse

Instance Method Details

#and_call_originalnil

Note:

This is only available on partial doubles.

Tells the object to delegate to the original unmodified method when it receives the message.

Examples:

expect(counter).to receive(:increment).and_call_original
original_count = counter.count
counter.increment
expect(counter.count).to eq(original_count + 1)

Returns:

  • (nil)

    No further chaining is supported after this.

141
142
143
144
145
146
147
148
# File 'lib/rspec/mocks/message_expectation.rb', line 141
def and_call_original
  block = lambda do |original, *args, &b|
    original.call(*args, &b)
  end
  block = block.ruby2_keywords if block.respond_to?(:ruby2_keywords)
  wrap_original(__method__, &block)
end

#and_invoke(first_proc, *procs) ⇒ nil

Tells the object to invoke a Proc when it receives the message. Given more than one value, the result of the first Proc is returned the first time the message is received, the result of the second Proc is returned the next time, etc, etc.

If the message is received more times than there are Procs, the result of the last Proc is returned for every subsequent call.

Examples:

allow(api).to receive(:get_foo).and_invoke(-> { raise ApiTimeout })
api.get_foo # => raises ApiTimeout
api.get_foo # => raises ApiTimeout

allow(api).to receive(:get_foo).and_invoke(-> { raise ApiTimeout }, -> { raise ApiTimeout }, -> { :a_foo })
api.get_foo # => raises ApiTimeout
api.get_foo # => rasies ApiTimeout
api.get_foo # => :a_foo
api.get_foo # => :a_foo
api.get_foo # => :a_foo
# etc

Returns:

  • (nil)

    No further chaining is supported after this.

109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/rspec/mocks/message_expectation.rb', line 109
def and_invoke(first_proc, *procs)
  raise_already_invoked_error_if_necessary(__method__)
  if negative?
    raise "`and_invoke` is not supported with negative message expectations"
  end
  if block_given?
    raise ArgumentError, "Implementation blocks aren't supported with `and_invoke`"
  end
  procs.unshift(first_proc)
  if procs.any? { |p| !p.respond_to?(:call) }
    raise ArgumentError, "Arguments to `and_invoke` must be callable."
  end
  @expected_received_count = [@expected_received_count, procs.size].max unless ignoring_args? || (@expected_received_count == 0 && @at_least)
  self.terminal_implementation_action = AndInvokeImplementation.new(procs)
  nil
end

#and_raisenil #and_raise(ExceptionClass) ⇒ nil #and_raise(ExceptionClass, message) ⇒ nil #and_raise(exception_instance) ⇒ nil

Note:

When you pass an exception class, the MessageExpectation will raise an instance of it, creating it with exception and passing message if specified. If the exception class initializer requires more than one parameters, you must pass in an instance and not the class, otherwise this method will raise an ArgumentError exception.

Tells the object to raise an exception when the message is received.

Examples:

allow(car).to receive(:go).and_raise
allow(car).to receive(:go).and_raise(OutOfGas)
allow(car).to receive(:go).and_raise(OutOfGas, "At least 2 oz of gas needed to drive")
allow(car).to receive(:go).and_raise(OutOfGas.new(2, :oz))

Returns:

  • (nil)

    No further chaining is supported after this.

186
187
188
189
190
# File 'lib/rspec/mocks/message_expectation.rb', line 186
def and_raise(*args)
  raise_already_invoked_error_if_necessary(__method__)
  self.terminal_implementation_action = Proc.new { raise(*args) }
  nil
end

#and_return(value) ⇒ nil #and_return(first_value, second_value) ⇒ nil

Tells the object to return a value when it receives the message. Given more than one value, the first value is returned the first time the message is received, the second value is returned the next time, etc, etc.

If the message is received more times than there are values, the last value is returned for every subsequent call.

Examples:

allow(counter).to receive(:count).and_return(1)
counter.count # => 1
counter.count # => 1

allow(counter).to receive(:count).and_return(1,2,3)
counter.count # => 1
counter.count # => 2
counter.count # => 3
counter.count # => 3
counter.count # => 3
# etc

Returns:

  • (nil)

    No further chaining is supported after this.

71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/rspec/mocks/message_expectation.rb', line 71
def and_return(first_value, *values)
  raise_already_invoked_error_if_necessary(__method__)
  if negative?
    raise "`and_return` is not supported with negative message expectations"
  end
  if block_given?
    raise ArgumentError, "Implementation blocks aren't supported with `and_return`"
  end
  values.unshift(first_value)
  @expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0 && @at_least)
  self.terminal_implementation_action = AndReturnImplementation.new(values)
  nil
end

#and_throw(symbol) ⇒ nil #and_throw(symbol, object) ⇒ nil

Tells the object to throw a symbol (with the object if that form is used) when the message is received.

Examples:

allow(car).to receive(:go).and_throw(:out_of_gas)
allow(car).to receive(:go).and_throw(:out_of_gas, :level => 0.1)

Returns:

  • (nil)

    No further chaining is supported after this.

202
203
204
205
206
# File 'lib/rspec/mocks/message_expectation.rb', line 202
def and_throw(*args)
  raise_already_invoked_error_if_necessary(__method__)
  self.terminal_implementation_action = Proc.new { throw(*args) }
  nil
end

#and_wrap_original(&block) ⇒ nil

Note:

This is only available on partial doubles.

Decorates the stubbed method with the supplied block. The original unmodified method is passed to the block along with any method call arguments so you can delegate to it, whilst still being able to change what args are passed to it and/or change the return value.

Examples:

expect(api).to receive(:large_list).and_wrap_original do |original_method, *args, &block|
  original_method.call(*args, &block).first(10)
end

Returns:

  • (nil)

    No further chaining is supported after this.

162
163
164
# File 'lib/rspec/mocks/message_expectation.rb', line 162
def and_wrap_original(&block)
  wrap_original(__method__, &block)
end

#and_yield(*args) {|@eval_context = Object.new| ... } ⇒ MessageExpectation

Tells the object to yield one or more args to a block when the message is received.

Examples:

stream.stub(:open).and_yield(StringIO.new)

Yields:

  • (@eval_context = Object.new)

Returns:

214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/rspec/mocks/message_expectation.rb', line 214
def and_yield(*args, &block)
  raise_already_invoked_error_if_necessary(__method__)
  yield @eval_context = Object.new if block
  # Initialize args to yield now that it's being used, see also: comment
  # in constructor.
  @args_to_yield ||= []
  @args_to_yield << args
  self.initial_implementation_action = AndYieldImplementation.new(@args_to_yield, @eval_context, @error_generator)
  self
end

#at_least(n, &block) ⇒ MessageExpectation

Constrain a message expectation to be received at least a specific number of times.

Examples:

expect(dealer).to receive(:deal_card).at_least(9).times

Returns:

249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/rspec/mocks/message_expectation.rb', line 249
def at_least(n, &block)
  raise_already_invoked_error_if_necessary(__method__)
  set_expected_received_count :at_least, n
  if n == 0
    raise "at_least(0) has been removed, use allow(...).to receive(:message) instead"
  end
  self.inner_implementation_action = block
  self
end

#at_most(n, &block) ⇒ MessageExpectation

Constrain a message expectation to be received at most a specific number of times.

Examples:

expect(dealer).to receive(:deal_card).at_most(10).times

Returns:

268
269
270
271
272
273
# File 'lib/rspec/mocks/message_expectation.rb', line 268
def at_most(n, &block)
  raise_already_invoked_error_if_necessary(__method__)
  self.inner_implementation_action = block
  set_expected_received_count :at_most, n
  self
end

#exactly(n, &block) ⇒ MessageExpectation

Constrain a message expectation to be received a specific number of times.

Examples:

expect(dealer).to receive(:deal_card).exactly(10).times

Returns:

236
237
238
239
240
241
# File 'lib/rspec/mocks/message_expectation.rb', line 236
def exactly(n, &block)
  raise_already_invoked_error_if_necessary(__method__)
  self.inner_implementation_action = block
  set_expected_received_count :exactly, n
  self
end

#neverMessageExpectation

Expect a message not to be received at all.

Examples:

expect(car).to receive(:stop).never

Returns:

293
294
295
296
297
# File 'lib/rspec/mocks/message_expectation.rb', line 293
def never
  error_generator.raise_double_negation_error("expect(obj)") if negative?
  @expected_received_count = 0
  self
end

#once(&block) ⇒ MessageExpectation

Expect a message to be received exactly one time.

Examples:

expect(car).to receive(:go).once

Returns:

304
305
306
307
308
# File 'lib/rspec/mocks/message_expectation.rb', line 304
def once(&block)
  self.inner_implementation_action = block
  set_expected_received_count :exactly, 1
  self
end

#ordered(&block) ⇒ MessageExpectation

Expect messages to be received in a specific order.

Examples:

expect(api).to receive(:prepare).ordered
expect(api).to receive(:run).ordered
expect(api).to receive(:finish).ordered

Returns:

379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
# File 'lib/rspec/mocks/message_expectation.rb', line 379
def ordered(&block)
  if type == :stub
    RSpec.warning(
      "`allow(...).to receive(..).ordered` is not supported and will " \
      "have no effect, use `and_return(*ordered_values)` instead."
    )
  end
  self.inner_implementation_action = block
  additional_expected_calls.times do
    @order_group.register(self)
  end
  @ordered = true
  self
end

#thrice(&block) ⇒ MessageExpectation

Expect a message to be received exactly three times.

Examples:

expect(car).to receive(:go).thrice

Returns:

326
327
328
329
330
# File 'lib/rspec/mocks/message_expectation.rb', line 326
def thrice(&block)
  self.inner_implementation_action = block
  set_expected_received_count :exactly, 3
  self
end

#times(&block) ⇒ MessageExpectation Also known as: time

Syntactic sugar for exactly, at_least and at_most

Examples:

expect(dealer).to receive(:deal_card).exactly(10).times
expect(dealer).to receive(:deal_card).at_least(10).times
expect(dealer).to receive(:deal_card).at_most(10).times

Returns:

282
283
284
285
# File 'lib/rspec/mocks/message_expectation.rb', line 282
def times(&block)
  self.inner_implementation_action = block
  self
end

#to_sString Also known as: inspect

Returns a nice representation of the message expectation.

Returns:

  • (String)

    a nice representation of the message expectation

396
397
398
399
400
# File 'lib/rspec/mocks/message_expectation.rb', line 396
def to_s
  args_description = error_generator.method_call_args_description(@argument_list_matcher.expected_args, "", "") { true }
  args_description = "(#{args_description})" unless args_description.start_with?("(")
  "#<#{self.class} #{error_generator.intro}.#{message}#{args_description}>"
end

#twice(&block) ⇒ MessageExpectation

Expect a message to be received exactly two times.

Examples:

expect(car).to receive(:go).twice

Returns:

315
316
317
318
319
# File 'lib/rspec/mocks/message_expectation.rb', line 315
def twice(&block)
  self.inner_implementation_action = block
  set_expected_received_count :exactly, 2
  self
end

#with(*args, &block) ⇒ MessageExpectation

Constrains a stub or message expectation to invocations with specific arguments.

With a stub, if the message might be received with other args as well, you should stub a default value first, and then stub or mock the same message using with to constrain to specific arguments.

A message expectation will fail if the message is received with different arguments.

Examples:

allow(cart).to receive(:add) { :failure }
allow(cart).to receive(:add).with(Book.new(:isbn => 1934356379)) { :success }
cart.add(Book.new(:isbn => 1234567890))
# => :failure
cart.add(Book.new(:isbn => 1934356379))
# => :success

expect(cart).to receive(:add).with(Book.new(:isbn => 1934356379)) { :success }
cart.add(Book.new(:isbn => 1234567890))
# => failed expectation
cart.add(Book.new(:isbn => 1934356379))
# => passes

Returns:

359
360
361
362
363
364
365
366
367
368
369
# File 'lib/rspec/mocks/message_expectation.rb', line 359
def with(*args, &block)
  raise_already_invoked_error_if_necessary(__method__)
  if args.empty?
    raise ArgumentError,
          "`with` must have at least one argument. Use `no_args` matcher to set the expectation of receiving no arguments."
  end
  self.inner_implementation_action = block
  @argument_list_matcher = ArgumentListMatcher.new(*args)
  self
end