Module: Mocoso

Defined in:
lib/mocoso.rb

Overview

Yet Another Simple Stub & Mock library, that also:

- Always restore stubbed methods to their original implementations.
- Doesn't allow to stub or mock undefined methods.
- Doesn't monkey-patch any class or object.
- Test-framework agnostic (Doesn't need integration code).

Setup

Execute:

gem install mocoso

Usage

Quick start:

require "cutest"
require "mocoso"

include Mocoso

test "mocking a class method" do
  user = User.new

  expect(User, :find, with: [1], return: user) do
    assert_equal user, User.find(1)
  end

  assert_equal nil, User.find(1)
end

test "stubbing an instance method" do
  user = User.new

  stub(user, :valid?, true) do
    assert user.valid?
  end

  assert !user.valid?
end

Note: this example uses the test framework Cutest:

Mocoso is inspired by Override, Minitest::Mock and Mocha.

Constant Summary collapse

ExpectationError =

Raised by #expect when a expectation is not fulfilled.

Mocoso.expect(object, :method, with: "argument", return: nil) do
  object.method("unexpected argument")
  # => Mocoso::ExpectationError:
  #     Expected ["argument"], got ["unexpected argument"]
end
Class.new(StandardError)

Instance Method Summary collapse

Instance Method Details

#expect(object, method, options, &block) ⇒ Object

Expects that method method is called with the arguments specified in the :with option (defaults to [] if it’s not given) and return the value specified in the :return option. If expectations are not met, it raises Mocoso::ExpectationError error. It uses #stub internally, so it will restore the methods to their original implementation after the block is executed.

class User < Model
end

user = User[1]

Mocoso.expect(user, :update, with: [:age, 18], return: true) do
  user.update(:name, "new name")
  # => Mocoso::ExpectationError:
  #     Expected [:age, 18], got [:name, "new name"]

  user.update(:age, 18)
  # => true
end


113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/mocoso.rb', line 113

def expect(object, method, options, &block)
  expectation = ->(*params) do
    with = options.fetch(:with, [])

    if params != with
      raise ExpectationError, "Expected #{ with }, got #{ params }"
    end

    options.fetch(:return)
  end

  stub(object, method, expectation, &block)
end

#stub(object, method, result) ⇒ Object

Rewrites each method from methods and defined in object. methods is a Hash that represents stubbed method name symbols as keys and corresponding return values as values. The methods are restored after the given block is executed.

 = SignupForm.new params[:user]

.valid? # => false

Mocoso.stub(, :valid?, true) do
  .valid? # => true
end

You can pass a callable object (responds to call) as a value:

Mocoso.stub(subject, :foo, -> { "foo" }) do
  subject.foo # => "foo"
end

Mocoso.stub(subject, :bar, ->(value) { value }) do
  subject.bar("foo") # => "foo"
end


78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/mocoso.rb', line 78

def stub(object, method, result)
  metaclass = object.singleton_class
  original = object.method(method)

  metaclass.send(:undef_method, method)
  metaclass.send(:define_method, method) do |*args|
    result.respond_to?(:call) ? result.call(*args) : result
  end

  yield
ensure
  metaclass.send(:undef_method, method)
  metaclass.send(:define_method, method, original)
end