class Mocha::Mock

explicitly define a total or partial ordering of invocations.
different mock objects, use the {Expectation#in_sequence} method to
If you want to specify more complex ordering or order invocations across
object.expected_method # => raises exception of class Exception1
object.expected_method # => 2
object.expected_method # => 1
object.stubs(:expected_method).returns(1, 2).then.raises(Exception)
object = mock()
@example
{Expectation#then} if desired).
{Expectation#returns} and {Expectation#raises} (along with syntactic sugar
actions for a method. You can also chain multiple calls to
{Expectation#returns} method with multiple arguments to create multiple
same method with exactly the same matchers. Instead, use the
The best thing to do is not set up multiple expectations and stubs for the
rather than the same order.
invoked in the opposite order than that in which they were specified,
- if you create different expectations for the same method, they will be
instead, possibly masking test failures.
expectation will match, and when it stops matching the stub will be used
- if you create a stub and then an expectation for the same method, the
be met.
stub will always override the expectation and the expectation will never
- if you create an expectation and then a stub for the same method, the
However, there are some possible “gotchas” caused by this scheme:
described below.
{Expectation#returns} method with multiple arguments to do this, as
action per invocation. However, it’s better to use the
- Set up different once expectations for the same method with different
override some of those stubs in individual tests.
- Set up default stubs in your the setup method of your test class and
This scheme allows you to:
against invocations.
while an +expects(:foo).at_least_once+ expectation will always be matched
expectation only matches once and will be ignored on future invocations
matching further invocations. For example, an +expects(:foo).once+
invocation. After the invocation, the matching expectation might stop
its expectations from newest to oldest to find one that matches the
When a method is invoked on a mock object, the mock object searches through
sugar to make the intent of the test more explicit.
expectation of zero or more invocations. The {#stubs} method is syntactic
Stubs and expectations are basically the same thing. A stub is just an
NoMethodError upon invocation otherwise.
will actually respond to the methods being stubbed, throwing a
making it a safer verifying mock. They check that the underlying responder
They force the mock to indicate what it is supposed to be mocking, thus
can therefore, be chained to the original creation methods in {API}.
{responds_like} and {responds_like_instance_of} both return a {Mock}, and
by methods on {Expectation}.
{expects} and {stubs} return an {Expectation} which can be further modified
Traditional mock object.

def __expectations__

Other tags:
    Private: -
def __expectations__
  @expectations
end

def __expire__(origin)

Other tags:
    Private: -
def __expire__(origin)
  @expired = origin || true
end

def __verified__?(assertion_counter = nil)

Other tags:
    Private: -
def __verified__?(assertion_counter = nil)
  @expectations.verified?(assertion_counter)
end

def all_expectations

Other tags:
    Private: -
def all_expectations
  @receiver.mocks.inject(ExpectationList.new) { |e, m| e + m.__expectations__ }
end

def any_expectations?

Other tags:
    Private: -
def any_expectations?
  @expectations.any?
end

def check_expiry

def check_expiry
  return unless @expired
  origin = @expired == true ? 'one test' : @expired
  sentences = [
    "#{mocha_inspect} was instantiated in #{origin} but it is receiving invocations within another test.",
    'This can lead to unintended interactions between tests and hence unexpected test failures.',
    'Ensure that every test correctly cleans up any state that it introduces.'
  ]
  raise StubbingError.new(sentences.join(' '), caller)
end

def check_responder_responds_to(symbol)

def check_responder_responds_to(symbol)
  if @responder && !@responder.respond_to?(symbol) # rubocop:disable Style/GuardClause
    raise NoMethodError, "undefined method `#{symbol}' for #{mocha_inspect} which responds like #{@responder.mocha_inspect}"
  end
end

def ensure_method_not_already_defined(method_name)

Other tags:
    Private: -
def ensure_method_not_already_defined(method_name)
  __singleton_class__.send(:undef_method, method_name) if __singleton_class__.method_defined?(method_name) || __singleton_class__.private_method_defined?(method_name)
end

def expects(method_name_or_hash, backtrace = nil)

Other tags:
    Example: Setup multiple expectations using +expected_methods_vs_return_values+. -
    Example: Expected method invoked twice so error raised -
    Example: Expected method not invoked so error raised -
    Example: Expected method invoked once so no error raised -

Parameters:
  • expected_methods_vs_return_values (Hash) -- expected method name symbols as keys and corresponding return values as values - these expectations are setup as if {#expects} were called multiple times.
  • method_name (Symbol, String) -- name of expected method

Overloads:
  • def expects(expected_methods_vs_return_values)
  • def expects(method_name)

Returns:
  • (Expectation) - last-built expectation which can be further modified by methods on {Expectation}.
def expects(method_name_or_hash, backtrace = nil)
  expectation = nil
  iterator = ArgumentIterator.new(method_name_or_hash)
  iterator.each do |*args|
    method_name = args.shift
    ensure_method_not_already_defined(method_name)
    expectation = Expectation.new(self, method_name, backtrace)
    expectation.in_sequence(@mockery.sequences.last) if @mockery.sequences.any?
    expectation.returns(args.shift) unless args.empty?
    @expectations.add(expectation)
  end
  expectation
end

def handle_method_call(symbol, arguments, block)

Other tags:
    Private: -
def handle_method_call(symbol, arguments, block)
  check_expiry
  check_responder_responds_to(symbol)
  invocation = Invocation.new(self, symbol, arguments, block)
  if (matching_expectation_allowing_invocation = all_expectations.match_allowing_invocation(invocation))
    matching_expectation_allowing_invocation.invoke(invocation)
  elsif (matching_expectation = all_expectations.match(invocation, ignoring_order: true)) || (!matching_expectation && !@everything_stubbed)
    raise_unexpected_invocation_error(invocation, matching_expectation)
  end
end

def initialize(mockery, name = nil, receiver = nil)

Other tags:
    Private: -
def initialize(mockery, name = nil, receiver = nil)
  @mockery = mockery
  @name = name || DefaultName.new(self)
  @receiver = receiver || DefaultReceiver.new(self)
  @expectations = ExpectationList.new
  @everything_stubbed = false
  @responder = nil
  @unexpected_invocation = nil
  @expired = false
end

def inspect

Other tags:
    Private: -
def inspect
  mocha_inspect
end

def method_missing(symbol, *arguments, &block) # rubocop:disable Style/MethodMissingSuper

Other tags:
    Private: -
def method_missing(symbol, *arguments, &block) # rubocop:disable Style/MethodMissingSuper
  handle_method_call(symbol, arguments, block)
end

def mocha_inspect

Other tags:
    Private: -
def mocha_inspect
  @name.mocha_inspect
end

def raise_unexpected_invocation_error(invocation, matching_expectation)

def raise_unexpected_invocation_error(invocation, matching_expectation)
  if @unexpected_invocation.nil?
    @unexpected_invocation = invocation
    matching_expectation.invoke(invocation) if matching_expectation
    call_description = @unexpected_invocation.call_description
    if matching_expectation && !matching_expectation.in_correct_order?
      call_description += ' invoked out of order'
    end
    message = "#{call_description}\n#{@mockery.mocha_inspect}"
  else
    message = @unexpected_invocation.short_call_description
  end
  raise ExpectationErrorFactory.build("unexpected invocation: #{message}", caller)
end

def respond_to_missing?(symbol, _include_all)

Other tags:
    Private: -
def respond_to_missing?(symbol, _include_all)
  if @responder
    @responder.respond_to?(symbol)
  else
    @everything_stubbed || all_expectations.matches_method?(symbol)
  end
end

def responds_like(responder)

Other tags:
    Example: Using {#responds_like} with a class method -
    Example: Using {#responds_like} with an instance method -
    Example: Normal mocking -

Other tags:
    See: #responds_like_instance_of -

Returns:
  • (Mock) - the same {Mock} instance, thereby allowing invocations of other {Mock} methods to be chained.

Parameters:
  • responder (Object, #respond_to?) -- an object used to determine whether {Mock} instance should +#respond_to?+ to an invocation.
def responds_like(responder)
  @responder = responder
  self
end

def responds_like_instance_of(responder_class)

Other tags:
    Example: Using {#responds_like_instance_of} -

Other tags:
    See: #responds_like -

Returns:
  • (Mock) - the same {Mock} instance, thereby allowing invocations of other {Mock} methods to be chained.

Parameters:
  • responder_class (Class) -- a class used to determine whether {Mock} instance should +#respond_to?+ to an invocation.
def responds_like_instance_of(responder_class)
  responds_like(responder_class.allocate)
end

def stub_everything

Other tags:
    Private: -
def stub_everything
  @everything_stubbed = true
end

def stubs(method_name_or_hash, backtrace = nil)

Other tags:
    Example: Setup multiple expectations using +stubbed_methods_vs_return_values+. -
    Example: No error raised however many times stubbed method is invoked -

Parameters:
  • stubbed_methods_vs_return_values (Hash) -- stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {#stubs} were called multiple times.
  • method_name (Symbol, String) -- name of stubbed method

Overloads:
  • def stubs(stubbed_methods_vs_return_values)
  • def stubs(method_name)

Returns:
  • (Expectation) - last-built expectation which can be further modified by methods on {Expectation}.
def stubs(method_name_or_hash, backtrace = nil)
  expectation = nil
  iterator = ArgumentIterator.new(method_name_or_hash)
  iterator.each do |*args|
    method_name = args.shift
    ensure_method_not_already_defined(method_name)
    expectation = Expectation.new(self, method_name, backtrace)
    expectation.at_least(0)
    expectation.in_sequence(@mockery.sequences.last) if @mockery.sequences.any?
    expectation.returns(args.shift) unless args.empty?
    @expectations.add(expectation)
  end
  expectation
end

def unstub(*method_names)

Other tags:
    Example: Unstubbing multiple methods. -
    Example: Invoking an unstubbed method causes error to be raised -

Parameters:
  • method_names (Array) -- names of methods to unstub.
def unstub(*method_names)
  method_names.each do |method_name|
    @expectations.remove_all_matching_method(method_name)
  end
end