class RSpec::Mocks::MessageExpectation
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.
Public Instance Methods
Tells the object to delegate to the original unmodified method when it receives the message.
@note This is only available on partial doubles.
@return [nil] No further chaining is supported after this. @example
expect(counter).to receive(:increment).and_call_original original_count = counter.count counter.increment expect(counter.count).to eq(original_count + 1)
# File rspec-mocks/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
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.
@return [nil] No further chaining is supported after this. @example
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 # => raises ApiTimeout api.get_foo # => :a_foo api.get_foo # => :a_foo api.get_foo # => :a_foo # etc
# File rspec-mocks/lib/rspec/mocks/message_expectation.rb, line 109 def and_invoke(first_proc, *procs, &_block) 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
@overload and_raise
@overload and_raise
(ExceptionClass) @overload and_raise
(ExceptionClass, message) @overload and_raise
(exception_instance)
Tells the object to raise an exception when the message is received.
@return [nil] No further chaining is supported after this. @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.
@example
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))
# File rspec-mocks/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
@overload and_return
(value) @overload and_return
(first_value, second_value)
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.
@return [nil] No further chaining is supported after this. @example
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
# File rspec-mocks/lib/rspec/mocks/message_expectation.rb, line 71 def and_return(first_value, *values, &_block) 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
@overload and_throw
(symbol) @overload and_throw
(symbol, object)
Tells the object to throw a symbol (with the object if that form is used) when the message is received.
@return [nil] No further chaining is supported after this. @example
allow(car).to receive(:go).and_throw(:out_of_gas) allow(car).to receive(:go).and_throw(:out_of_gas, :level => 0.1)
# File rspec-mocks/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
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.
@note This is only available on partial doubles.
@return [nil] No further chaining is supported after this. @example
expect(api).to receive(:large_list).and_wrap_original do |original_method, *args, &block| original_method.call(*args, &block).first(10) end
# File rspec-mocks/lib/rspec/mocks/message_expectation.rb, line 162 def and_wrap_original(&block) wrap_original(__method__, &block) end
Tells the object to yield one or more args to a block when the message is received.
@return [MessageExpectation] self, to support further chaining. @example
stream.stub(:open).and_yield(StringIO.new)
# File rspec-mocks/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
Constrain a message expectation to be received at least a specific number of times.
@return [MessageExpectation] self, to support further chaining. @example
expect(dealer).to receive(:deal_card).at_least(9).times
# File rspec-mocks/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
Constrain a message expectation to be received at most a specific number of times.
@return [MessageExpectation] self, to support further chaining. @example
expect(dealer).to receive(:deal_card).at_most(10).times
# File rspec-mocks/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
Constrain a message expectation to be received a specific number of times.
@return [MessageExpectation] self, to support further chaining. @example
expect(dealer).to receive(:deal_card).exactly(10).times
# File rspec-mocks/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
Expect a message not to be received at all.
@return [MessageExpectation] self, to support further chaining. @example
expect(car).to receive(:stop).never
# File rspec-mocks/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
Expect a message to be received exactly one time.
@return [MessageExpectation] self, to support further chaining. @example
expect(car).to receive(:go).once
# File rspec-mocks/lib/rspec/mocks/message_expectation.rb, line 304 def once(&block) self.inner_implementation_action = block set_expected_received_count :exactly, 1 self end
Expect messages to be received in a specific order.
@return [MessageExpectation] self, to support further chaining. @example
expect(api).to receive(:prepare).ordered expect(api).to receive(:run).ordered expect(api).to receive(:finish).ordered
# File rspec-mocks/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
Expect a message to be received exactly three times.
@return [MessageExpectation] self, to support further chaining. @example
expect(car).to receive(:go).thrice
# File rspec-mocks/lib/rspec/mocks/message_expectation.rb, line 326 def thrice(&block) self.inner_implementation_action = block set_expected_received_count :exactly, 3 self end
Syntactic sugar for ‘exactly`, `at_least` and `at_most`
@return [MessageExpectation] self, to support further chaining. @example
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
# File rspec-mocks/lib/rspec/mocks/message_expectation.rb, line 282 def times(&block) self.inner_implementation_action = block self end
@return [String] a nice representation of the message expectation
# File rspec-mocks/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
Expect a message to be received exactly two times.
@return [MessageExpectation] self, to support further chaining. @example
expect(car).to receive(:go).twice
# File rspec-mocks/lib/rspec/mocks/message_expectation.rb, line 315 def twice(&block) self.inner_implementation_action = block set_expected_received_count :exactly, 2 self end
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.
@return [MessageExpectation] self, to support further chaining. @example
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
# File rspec-mocks/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