class RSpec::Mocks::MessageExpectation

Attributes

error_generator[RW]
expected_from[W]
expected_received_count[W]
method_block[W]
sym[R]

@private

Public Class Methods

new(error_generator, expectation_ordering, expected_from, sym, method_block, expected_received_count=1, opts={}, &implementation) click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 13
def initialize(error_generator, expectation_ordering, expected_from, sym, method_block, expected_received_count=1, opts={}, &implementation)
  @error_generator = error_generator
  @error_generator.opts = opts
  @expected_from = expected_from
  @sym = sym
  @method_block = method_block
  @return_block = nil
  @actual_received_count = 0
  @expected_received_count = expected_received_count
  @args_expectation = ArgumentExpectation.new(ArgumentMatchers::AnyArgsMatcher.new)
  @consecutive = false
  @exception_to_raise = nil
  @args_to_throw = []
  @order_group = expectation_ordering
  @at_least = nil
  @at_most = nil
  @exactly = nil
  @args_to_yield = []
  @failed_fast = nil
  @args_to_yield_were_cloned = false
  @return_block = implementation
  @eval_context = nil
end

Public Instance Methods

actual_received_count_matters?() click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 405
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 254
def advise(*args)
  similar_messages << args
end
and_raise(exception=Exception) click to toggle source

@overload #and_raise @overload #and_raise(ExceptionClass) @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 %xnew`. If the exception class
initializer requires any parameters, you must pass in an instance and
not the class.

@example

car.stub(:go).and_raise
car.stub(:go).and_raise(OutOfGas)
car.stub(:go).and_raise(OutOfGas.new(2, :oz))
# File lib/rspec/mocks/message_expectation.rb, line 124
def and_raise(exception=Exception)
  @exception_to_raise = exception
end
and_return(*values, &return_block) click to toggle source

@overload #and_return(value) @overload #and_return(first_value, second_value) @overload #and_return(&block)

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.

The block format is still supported, but is unofficially deprecated in favor of just passing a block to the stub method.

@example

counter.stub(:count).and_return(1)
counter.count # => 1
counter.count # => 1

counter.stub(:count).and_return(1,2,3)
counter.count # => 1
counter.count # => 2
counter.count # => 3
counter.count # => 3
counter.count # => 3
# etc

# Supported, but ...
counter.stub(:count).and_return { 1 }
counter.count # => 1

# ... this is prefered
counter.stub(:count) { 1 }
counter.count # => 1
# File lib/rspec/mocks/message_expectation.rb, line 92
def and_return(*values, &return_block)
  Kernel::raise AmbiguousReturnError unless @method_block.nil?
  case values.size
  when 0 then value = nil
  when 1 then value = values[0]
  else
    value = values
    @consecutive = true
    @expected_received_count = values.size if !ignoring_args? &&
      @expected_received_count < values.size
  end
  @return_block = block_given? ? return_block : lambda { value }
end
and_throw(symbol, object = nil) 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

car.stub(:go).and_throw(:out_of_gas)
car.stub(:go).and_throw(:out_of_gas, :level => 0.1)
# File lib/rspec/mocks/message_expectation.rb, line 138
def and_throw(symbol, object = nil)
  @args_to_throw << symbol
  @args_to_throw << object if object
end
and_yield(*args) { |eval_context| ... } 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 149
def and_yield(*args, &block)
  if @args_to_yield_were_cloned
    @args_to_yield.clear
    @args_to_yield_were_cloned = false
  end

  if block
    @eval_context = Object.new
    @eval_context.extend RSpec::Mocks::InstanceExec
    yield @eval_context
  end

  @args_to_yield << args
  self
end
any_number_of_times(&block) click to toggle source

Allows an expected message to be received any number of times.

# File lib/rspec/mocks/message_expectation.rb, line 347
def any_number_of_times(&block)
  @method_block = block if block
  @expected_received_count = :any
  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

dealer.should_recieve(:deal_card).at_least(9).times
# File lib/rspec/mocks/message_expectation.rb, line 315
def at_least(n, &block)
  @method_block = block if block
  set_expected_received_count :at_least, n
  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

dealer.should_recieve(:deal_card).at_most(10).times
# File lib/rspec/mocks/message_expectation.rb, line 327
def at_most(n, &block)
  @method_block = block if block
  set_expected_received_count :at_most, n
  self
end
build_child(expected_from, method_block, expected_received_count, opts={}) click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 38
def build_child(expected_from, method_block, expected_received_count, opts={})
  child = clone
  child.expected_from = expected_from
  child.method_block = method_block
  child.expected_received_count = expected_received_count
  child.clear_actual_received_count!
  new_gen = error_generator.clone
  new_gen.opts = opts
  child.error_generator = new_gen
  child.clone_args_to_yield(*@args_to_yield)
  child
end
called_max_times?() click to toggle source

@private

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

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

@example

dealer.should_recieve(:deal_card).exactly(10).times
# File lib/rspec/mocks/message_expectation.rb, line 303
def exactly(n, &block)
  @method_block = block if block
  set_expected_received_count :exactly, n
  self
end
expected_args() click to toggle source

@private

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

@private

# File lib/rspec/mocks/message_expectation.rb, line 224
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 259
def generate_error
  if similar_messages.empty?
    @error_generator.raise_expectation_error(@sym, @expected_received_count, @actual_received_count, *@args_expectation.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 229
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 410
def increase_actual_received_count!
  @actual_received_count += 1
end
invoke(*args, &block) click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 171
def invoke(*args, &block)
  if @expected_received_count == 0
    @failed_fast = true
    @actual_received_count += 1
    @error_generator.raise_expectation_error(@sym, @expected_received_count, @actual_received_count, *args)
  end

  @order_group.handle_order_constraint self

  begin
    Kernel::raise(@exception_to_raise) unless @exception_to_raise.nil?
    Kernel::throw(*@args_to_throw) unless @args_to_throw.empty?

    default_return_val = if !@method_block.nil?
                           invoke_method_block(*args, &block)
                         elsif !@args_to_yield.empty? || @eval_context
                           invoke_with_yield(&block)
                         else
                           nil
                         end

    if @consecutive
      invoke_consecutive_return_block(*args, &block)
    elsif @return_block
      invoke_return_block(*args, &block)
    else
      default_return_val
    end
  ensure
    @actual_received_count += 1
  end
end
matches?(sym, *args) click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 166
def matches?(sym, *args)
  @sym == sym and @args_expectation.args_match?(*args)
end
matches_at_least_count?() click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 234
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 239
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 244
def matches_exact_count?
  @expected_received_count == @actual_received_count
end
matches_name_but_not_args(sym, *args) click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 211
def matches_name_but_not_args(sym, *args)
  @sym == sym and not @args_expectation.args_match?(*args)
end
negative_expectation_for?(sym) click to toggle source

@private

# File lib/rspec/mocks/message_expectation.rb, line 400
def negative_expectation_for?(sym)
  return false
end
never() click to toggle source

Expect a message not to be received at all.

@example

car.should_receive(:stop).never
# File lib/rspec/mocks/message_expectation.rb, line 358
def never
  @expected_received_count = 0
  self
end
once(&block) click to toggle source

Expect a message to be received exactly one time.

@example

car.should_receive(:go).once
# File lib/rspec/mocks/message_expectation.rb, line 368
def once(&block)
  @method_block = block if 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

api.should_receive(:prepare).ordered
api.should_receive(:run).ordered
api.should_receive(:finish).ordered
# File lib/rspec/mocks/message_expectation.rb, line 392
def ordered(&block)
  @method_block = block if block
  @order_group.register(self)
  @ordered = true
  self
end
similar_messages() click to toggle source

@private

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

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

@example

dealer.should_recieve(:deal_card).exactly(10).times
dealer.should_recieve(:deal_card).at_least(10).times
dealer.should_recieve(:deal_card).at_most(10).times
# File lib/rspec/mocks/message_expectation.rb, line 340
def times(&block)
  @method_block = block if block
  self
end
twice(&block) click to toggle source

Expect a message to be received exactly two times.

@example

car.should_receive(:go).twice
# File lib/rspec/mocks/message_expectation.rb, line 379
def twice(&block)
  @method_block = block if 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 216
def verify_messages_received
  generate_error unless expected_messages_received? || failed_fast?
rescue RSpec::Mocks::MockExpectationError => error
  error.backtrace.insert(0, @expected_from)
  Kernel::raise error
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

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

cart.should_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 291
def with(*args, &block)
  @return_block = block if block_given? unless args.empty?
  @args_expectation = ArgumentExpectation.new(*args, &block)
  self
end

Protected Instance Methods

clear_actual_received_count!() click to toggle source
# File lib/rspec/mocks/message_expectation.rb, line 483
def clear_actual_received_count!
  @actual_received_count = 0
end
clone_args_to_yield(*args) click to toggle source
# File lib/rspec/mocks/message_expectation.rb, line 460
def clone_args_to_yield(*args)
  @args_to_yield = args.clone
  @args_to_yield_were_cloned = true
end
eval_block(*args, &block) click to toggle source
# File lib/rspec/mocks/message_expectation.rb, line 438
def eval_block(*args, &block)
  if @eval_context
    @eval_context.instance_exec(*args, &block)
  else
    block.call(*args)
  end
end
failed_fast?() click to toggle source
# File lib/rspec/mocks/message_expectation.rb, line 465
def failed_fast?
  @failed_fast
end
invoke_consecutive_return_block(*args, &block) click to toggle source
# File lib/rspec/mocks/message_expectation.rb, line 446
def invoke_consecutive_return_block(*args, &block)
  value = invoke_return_block(*args, &block)
  index = [@actual_received_count, value.size-1].min
  value[index]
end
invoke_method_block(*args, &block) click to toggle source
# File lib/rspec/mocks/message_expectation.rb, line 416
def invoke_method_block(*args, &block)
  begin
    @method_block.call(*args, &block)
  rescue => detail
    @error_generator.raise_block_failed_error(@sym, detail.message)
  end
end
invoke_return_block(*args, &block) click to toggle source
# File lib/rspec/mocks/message_expectation.rb, line 452
def invoke_return_block(*args, &block)
  args << block unless block.nil?
  # Ruby 1.9 - when we set @return_block to return values
  # regardless of arguments, any arguments will result in
  # a "wrong number of arguments" error
  @return_block.arity == 0 ? @return_block.call : @return_block.call(*args)
end
invoke_with_yield(&block) click to toggle source
# File lib/rspec/mocks/message_expectation.rb, line 424
def invoke_with_yield(&block)
  if block.nil?
    @error_generator.raise_missing_block_error @args_to_yield
  end
  value = nil
  @args_to_yield.each do |args_to_yield_this_time|
    if block.arity > -1 && args_to_yield_this_time.length != block.arity
      @error_generator.raise_wrong_arity_error args_to_yield_this_time, block.arity
    end
    value = eval_block(*args_to_yield_this_time, &block)
  end
  value
end
set_expected_received_count(relativity, n) click to toggle source
# File lib/rspec/mocks/message_expectation.rb, line 469
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
                               n
                             when :once
                               1
                             when :twice
                               2
                             end
end