class RSpec::Mocks::MessageExpectation

@private

Attributes

argument_list_matcher[W]
error_generator[RW]

@private

expected_from[W]
expected_received_count[W]
implementation[RW]

@private

message[R]
orig_object[R]

Public Class Methods

new(error_generator, expectation_ordering, expected_from, method_double, type=:expectation, opts={}, &implementation_block) click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 49
def initialize(error_generator, expectation_ordering, expected_from, method_double,
               type=:expectation, opts={}, &implementation_block)
  @error_generator = error_generator
  @error_generator.opts = opts
  @expected_from = expected_from
  @method_double = method_double
  @orig_object = @method_double.object
  @message = @method_double.method_name
  @actual_received_count = 0
  @expected_received_count = type == :expectation ? 1 : :any
  @argument_list_matcher = ArgumentListMatcher::MATCH_ALL
  @order_group = expectation_ordering
  @order_group.register(self) unless type == :stub
  @ordered = false
  @at_least = @at_most = @exactly = nil
  @args_to_yield = []
  @failed_fast = nil
  @eval_context = nil
  @yield_receiver_to_implementation_block = false

  @implementation = Implementation.new
  self.inner_implementation_action = implementation_block
end

Public Instance Methods

actual_received_count_matters?() click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 456
def actual_received_count_matters?
  @at_least || @at_most || @exactly
end
advise(*args) click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 282
def advise(*args)
  similar_messages << args
end
and_call_original() click to toggle source

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

@note This is only available on partial doubles.

@example

expect(counter).to receive(:increment).and_call_original
original_count = counter.count
counter.increment
expect(counter.count).to eq(original_count + 1)
# File lib/rspec/mocks/message_expectation.rb, line 138
def and_call_original
  if RSpec::Mocks::TestDouble === @method_double.object
    @error_generator.raise_only_valid_on_a_partial_double(:and_call_original)
  else
    warn_about_stub_override if implementation.inner_action
    @implementation = AndCallOriginalImplementation.new(@method_double.original_method)
    @yield_receiver_to_implementation_block = false
  end
end
and_raise(exception = RuntimeError, message = nil) click to toggle source

@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.

@note

When you pass an exception class, the MessageExpectation will raise
an instance of it, creating it with %xexception` and passing %xmessage`
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 lib/rspec/mocks/message_expectation.rb, line 169
def and_raise(exception = RuntimeError, message = nil)
  if exception.respond_to?(:exception)
    exception = message ? exception.exception(message) : exception.exception
  end

  self.terminal_implementation_action = Proc.new { raise exception }
  nil
end
and_return(first_value, *values) click to toggle source

@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 received for every subsequent call.

@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 lib/rspec/mocks/message_expectation.rb, line 102
def and_return(first_value, *values)
  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 and @at_least)
  self.terminal_implementation_action = AndReturnImplementation.new(values)

  nil
end
and_throw(*args) click to toggle source

@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.

@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 lib/rspec/mocks/message_expectation.rb, line 188
def and_throw(*args)
  self.terminal_implementation_action = Proc.new { throw(*args) }
  nil
end
and_yield(*args) { |eval_context = object| ... } click to toggle source

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

@example

stream.stub(:open).and_yield(StringIO.new)
# File lib/rspec/mocks/message_expectation.rb, line 199
def and_yield(*args, &block)
  yield @eval_context = Object.new if block
  @args_to_yield << args
  self.initial_implementation_action = AndYieldImplementation.new(@args_to_yield, @eval_context, @error_generator)
  self
end
and_yield_receiver_to_implementation() click to toggle source
# File lib/rspec/mocks/message_expectation.rb, line 118
def and_yield_receiver_to_implementation
  @yield_receiver_to_implementation_block = true
  self
end
at_least(n, &block) click to toggle source

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

@example

expect(dealer).to receive(:deal_card).at_least(9).times
# File lib/rspec/mocks/message_expectation.rb, line 363
def at_least(n, &block)
  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) click to toggle source

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

@example

expect(dealer).to receive(:deal_card).at_most(10).times
# File lib/rspec/mocks/message_expectation.rb, line 381
def at_most(n, &block)
  self.inner_implementation_action = block
  set_expected_received_count :at_most, n
  self
end
called_max_times?() click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 227
def called_max_times?
  @expected_received_count != :any &&
    !@at_least &&
    @expected_received_count > 0 &&
    @actual_received_count >= @expected_received_count
end
description() click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 302
def description
  @error_generator.describe_expectation(@message, @expected_received_count, @actual_received_count, *expected_args)
end
ensure_expected_ordering_received!() click to toggle source
# File lib/rspec/mocks/message_expectation.rb, line 251
def ensure_expected_ordering_received!
  @order_group.verify_invocation_order(self) if @ordered
  true
end
exactly(n, &block) click to toggle source

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

@example

expect(dealer).to receive(:deal_card).exactly(10).times
# File lib/rspec/mocks/message_expectation.rb, line 351
def exactly(n, &block)
  self.inner_implementation_action = block
  set_expected_received_count :exactly, n
  self
end
expectation_count_type() click to toggle source
# File lib/rspec/mocks/message_expectation.rb, line 295
def expectation_count_type
  return :at_least if @at_least
  return :at_most if @at_most
  return nil
end
expected_args() click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 74
def expected_args
  @argument_list_matcher.expected_args
end
expected_messages_received?() click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 247
def expected_messages_received?
  ignoring_args? || matches_exact_count? || matches_at_least_count? || matches_at_most_count?
end
generate_error() click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 287
def generate_error
  if similar_messages.empty?
    @error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *expected_args)
  else
    @error_generator.raise_similar_message_args_error(self, *@similar_messages)
  end
end
ignoring_args?() click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 257
def ignoring_args?
  @expected_received_count == :any
end
increase_actual_received_count!() click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 461
def increase_actual_received_count!
  @actual_received_count += 1
end
invoke(parent_stub, *args, &block) click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 212
def invoke(parent_stub, *args, &block)
  invoke_incrementing_actual_calls_by(1, parent_stub, *args, &block)
end
invoke_without_incrementing_received_count(parent_stub, *args, &block) click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 217
def invoke_without_incrementing_received_count(parent_stub, *args, &block)
  invoke_incrementing_actual_calls_by(0, parent_stub, *args, &block)
end
matches?(message, *args) click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 207
def matches?(message, *args)
  @message == message && @argument_list_matcher.args_match?(*args)
end
matches_at_least_count?() click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 262
def matches_at_least_count?
  @at_least && @actual_received_count >= @expected_received_count
end
matches_at_most_count?() click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 267
def matches_at_most_count?
  @at_most && @actual_received_count <= @expected_received_count
end
matches_exact_count?() click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 272
def matches_exact_count?
  @expected_received_count == @actual_received_count
end
matches_name_but_not_args(message, *args) click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 235
def matches_name_but_not_args(message, *args)
  @message == message and not @argument_list_matcher.args_match?(*args)
end
negative?() click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 222
def negative?
  @expected_received_count == 0 && !@at_least
end
negative_expectation_for?(message) click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 451
def negative_expectation_for?(message)
  @message == message && negative?
end
never() click to toggle source

Expect a message not to be received at all.

@example

expect(car).to receive(:stop).never
# File lib/rspec/mocks/message_expectation.rb, line 404
def never
  ErrorGenerator.raise_double_negation_error("expect(obj)") if negative?
  @expected_received_count = 0
  self
end
once(&block) click to toggle source

Expect a message to be received exactly one time.

@example

expect(car).to receive(:go).once
# File lib/rspec/mocks/message_expectation.rb, line 415
def once(&block)
  self.inner_implementation_action = block
  set_expected_received_count :exactly, 1
  self
end
ordered(&block) click to toggle source

Expect messages to be received in a specific order.

@example

expect(api).to receive(:prepare).ordered
expect(api).to receive(:run).ordered
expect(api).to receive(:finish).ordered
# File lib/rspec/mocks/message_expectation.rb, line 439
def ordered(&block)
  self.inner_implementation_action = block
  @ordered = true
  self
end
ordered?() click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 446
def ordered?
  @ordered
end
raise_out_of_order_error() click to toggle source
# File lib/rspec/mocks/message_expectation.rb, line 306
def raise_out_of_order_error
  @error_generator.raise_out_of_order_error @message
end
similar_messages() click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 277
def similar_messages
  @similar_messages ||= []
end
times(&block) click to toggle source

Syntactic sugar for `exactly`, `at_least` and `at_most`

@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 lib/rspec/mocks/message_expectation.rb, line 394
def times(&block)
  self.inner_implementation_action = block
  self
end
twice(&block) click to toggle source

Expect a message to be received exactly two times.

@example

expect(car).to receive(:go).twice
# File lib/rspec/mocks/message_expectation.rb, line 426
def twice(&block)
  self.inner_implementation_action = block
  set_expected_received_count :exactly, 2
  self
end
verify_messages_received() click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 240
def verify_messages_received
  InsertOntoBacktrace.line(@expected_from) do
    generate_error unless expected_messages_received? || failed_fast?
  end
end
with(*args, &block) click to toggle source

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.

@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 lib/rspec/mocks/message_expectation.rb, line 334
def with(*args, &block)
  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
yield_receiver_to_implementation_block?() click to toggle source
# File lib/rspec/mocks/message_expectation.rb, line 123
def yield_receiver_to_implementation_block?
  @yield_receiver_to_implementation_block
end

Private Instance Methods

failed_fast?() click to toggle source
# File lib/rspec/mocks/message_expectation.rb, line 493
def failed_fast?
  @failed_fast
end
initial_implementation_action=(action) click to toggle source
# File lib/rspec/mocks/message_expectation.rb, line 508
def initial_implementation_action=(action)
  implementation.initial_action = action
end
inner_implementation_action=(action) click to toggle source
# File lib/rspec/mocks/message_expectation.rb, line 512
def inner_implementation_action=(action)
  return unless action
  warn_about_stub_override if implementation.inner_action
  implementation.inner_action = action
end
invoke_incrementing_actual_calls_by(increment, parent_stub, *args, &block) click to toggle source
# File lib/rspec/mocks/message_expectation.rb, line 467
def invoke_incrementing_actual_calls_by(increment, parent_stub, *args, &block)
  if yield_receiver_to_implementation_block?
    args.unshift(orig_object)
  end

  if negative? || ((@exactly || @at_most) && (@actual_received_count == @expected_received_count))
    @actual_received_count += increment
    @failed_fast = true
    #args are the args we actually received, @argument_list_matcher is the
    #list of args we were expecting
    @error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *args)
  end

  @order_group.handle_order_constraint self

  begin
    if implementation.present?
      implementation.call(*args, &block)
    elsif parent_stub
      parent_stub.invoke(nil, *args, &block)
    end
  ensure
    @actual_received_count += increment
  end
end
set_expected_received_count(relativity, n) click to toggle source
# File lib/rspec/mocks/message_expectation.rb, line 497
def set_expected_received_count(relativity, n)
  @at_least = (relativity == :at_least)
  @at_most  = (relativity == :at_most)
  @exactly  = (relativity == :exactly)
  @expected_received_count = case n
                             when Numeric then n
                             when :once   then 1
                             when :twice  then 2
                             end
end
terminal_implementation_action=(action) click to toggle source
# File lib/rspec/mocks/message_expectation.rb, line 518
def terminal_implementation_action=(action)
  implementation.terminal_action = action
end
warn_about_stub_override() click to toggle source
# File lib/rspec/mocks/message_expectation.rb, line 522
def warn_about_stub_override
  RSpec.warning(
    "You're overriding a previous stub implementation of `#{@message}`. " +
    "Called from #{CallerFilter.first_non_rspec_line}."
  )
end