Library / SDK
freerange/mocha avatar
freerange/mocha

freerange/mocha: Ruby mocking with an expects and a stubs, and a load order that matters

A mocking and stubbing library for Ruby

1,284 stars160 forksRubyNOASSERTION

At a glance

What is it?
Mocha is a Ruby library for mocking and stubbing that keeps to two of the four test double types, deliberately not fakes or spies, and ships adapters for Minitest, Test::Unit, RSpec, Cucumber and Rails. Its setup is mostly about ordering, since the Mocha integration has to be loaded after the test framework it plugs into.
Who is it for?
Adopt mocha if your Ruby suite already uses Minitest, Test::Unit or RSpec and you want mock objects with expectation verification rather than a hand rolled stub helper, since the syntax is small and the framework integration is built in. Do not adopt it if you need fakes or test spies, because the project states that it is focused on mocks and stubs and that extending it that way is a deliberate non-goal for now.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 33 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Two of the four test double types, on purpose

Mocha is a Ruby library for mocking and stubbing, and the very first bullet of its description is about what it is not. It is deliberately not, yet, faking or spying, linking to the xunitpatterns descriptions of the four test double types.

The intended usage section says the same thing in more detail. Mocha is for the Mock Object and Test Stub types of test double, not for the Fake Object or Test Spy types. The stated reason is focus: although it would be possible to extend Mocha to allow fakes and spies, the project has chosen to keep it focused on mocks and stubs.

That is a small decision with real consequences for a team, because the four types answer different questions. A mock asserts that an interaction happened, with an expectation that must be met. A stub replaces a method with a fixed answer and asserts nothing. A fake is a working implementation, a test spy records calls for later inspection. If your suite leans on spies to assert that a notification was sent, or on fakes to give a service a working in-memory implementation, Mocha is the wrong shape of tool, and no amount of configuration will change that.

What Mocha does offer is a unified, simple and readable syntax for both full and partial mocking, with built-in support for Minitest and Test::Unit, and support from a number of other test frameworks through adapters.

The setup is a load order, and it is the part people get wrong

Installation is a gem:

code
$ gem install mocha

The important instruction comes next. If you are intending to use Mocha with Test::Unit or Minitest, you should only set Mocha up after loading the relevant test library. The integration is a patch on top of the framework, so it has to be applied to a framework that already exists.

For Test::Unit that is three requires in order:

ruby
require 'rubygems'
gem 'mocha'
require 'test/unit'
require 'mocha/test_unit'

And for Minitest it is the same shape with a different last line:

ruby
require 'rubygems'
gem 'mocha'
require 'minitest/autorun'
require 'mocha/minitest'

With Bundler, the gem goes in the Gemfile and the require happens later, once you know the test library has been loaded:

ruby
# Gemfile
gem 'mocha'

# Elsewhere after Bundler has loaded gems e.g. after `require 'bundler/setup'`
require 'test/unit'
require 'mocha/test_unit'

The general rule for any other framework is stated in the other test framework section: ensure that the relevant Mocha file, `mocha/minitest`, `mocha/test_unit` or `mocha/api`, is required after the test framework has been loaded, for example at the bottom of `test_helper.rb` or `spec_helper.rb`, or at least after `rails/test_help` has been required.

For a Rails application the same advice applies with a note. If you load Mocha through Bundler inside Rails, you should set it up manually, at the bottom of your `test_helper.rb`, and the documentation points out that since Rails v4 `ActiveSupport::TestCase` has inherited from `Minitest::Test` or its earlier equivalents, so unless you are explicitly using Test::Unit you are likely using Minitest.

expects for expectations, stubs for answers

The syntax is the small part of the library, and the quick start shows the whole vocabulary.

ruby
class MiscExampleTest < Test::Unit::TestCase
  def test_mocking_a_class_method
    product = Product.new
    Product.expects(:find).with(1).returns(product)
    assert_equal product, Product.find(1)
  end

  def test_mocking_an_instance_method_on_a_real_object
    product = Product.new
    product.expects(:save).returns(true)
    assert product.save
  end

  def test_stubbing_instance_methods_on_real_objects
    prices = [stub(pence: 1000), stub(pence: 2000)]
    product = Product.new
    product.stubs(:prices).returns(prices)
    assert_equal [1000, 2000], product.prices.collect {|p| p.pence}
  end

  def test_traditional_mocking
    object = mock('object')
    object.expects(:expected_method).with(:p1, :p2).returns(:result)
    assert_equal :result, object.expected_method(:p1, :p2)
  end
end

`expects` sets an expectation: the method must be called with those arguments, and the mock is verified at the end of the test. `stubs` replaces a method with a return value and makes no demand about whether it was called. `mock('object')` creates a standalone mock object, and `stub(method1: :result1)` is the shortcut form that builds one with named methods.

The `with` argument is what makes a mock useful rather than merely possible, because it is how you say that `find` was called with 1 and not with 2. The mock object example in the documentation makes the verification explicit, with the comment that the expectation is auto-verified at the end of the test.

Partial mocking and any_instance

Full mocking means creating a mock and injecting it. Partial mocking means stubbing a method on a real object or class, which is what the README calls partial mocking, and the documentation illustrates it with an `Order` class whose `total_cost` depends on `total_weight` and `shipping_cost`.

The test then stubs one method and lets the rest of the object run:

ruby
class OrderTest < Test::Unit::TestCase
  # illustrates stubbing instance method
  def test_should_calculate_shipping_cost_based_on_total_weight
    order = Order.new
    order.stubs(:total_weight).returns(10)
    assert_equal 60, order.shipping_cost
  end
end

The class method case is the same idea, applied to a singleton method defined inside a `class << self` block, and the quick start also shows the per class variant, `Product.any_instance.stubs(:name).returns('stubbed_name')`, which applies the stub to every instance of the class.

That last form is the one to be careful with, and it is a style question rather than a library question. Stubbing every instance of a class is convenient and blunt: it removes the ability to test that one particular instance behaves differently, and it hides which object under test is actually responsible for a call. Partial mocking on a specific instance keeps the scope tight, and most suites that adopt Mocha end up using the narrower form for that reason.

RSpec, Cucumber and Rails, three different integration shapes

The other frameworks integrate in three quite different ways, and knowing which is which saves time.

RSpec includes a mocha adapter, and you tell RSpec you want to mock with `:mocha`:

ruby
# Within `spec/spec_helper.rb`
RSpec.configure do |config|
  config.mock_with :mocha
end

There is no require statement needed for the setup, because RSpec does it itself. That is a nicer integration than the others, and it is the model of what a framework with first class adapter support looks like.

Cucumber is the most manual, because a Cucumber step is not a test method and there is no framework to patch. The World object is given the Mocha API and each scenario is wrapped in an around hook that sets up, runs and verifies:

ruby
require 'mocha/api'

World(Mocha::API)

Around do |scenario, block|
  begin
    mocha_setup
    block.call
    mocha_verify
  ensure
    mocha_teardown
  end
end

The `ensure` block is what guarantees teardown runs even when the step fails, which is the kind of detail that separates a working integration from one that leaks state between scenarios.

Rails is the third case, and it is the one the documentation warns about, because the require has to come after Rails has loaded the test framework itself, and the documentation places it at the bottom of `test_helper.rb`. The instruction is to put the require at the bottom of the file, or at least after `rails/test_help`.

An old known issue and the state of the project

The README carries a known issues section, and the entry is about the distant past. Prior to v1.15.0, when support for Ruby v1.8 was dropped, stubbing an aliased class method where the original method is defined in a module used to `extend` the class did not work in Ruby v1.8, and the README points at the acceptance test that captures the behaviour.

That entry is a useful signal rather than a problem to worry about. A library that documents a bug in a Ruby version it dropped years ago is a library that has been around long enough to have made that decision deliberately, and the fact that the issue is still listed rather than deleted suggests the documentation is maintained rather than copied forward.

The repository layout supports that reading. There is a `lib/` directory for the code, a `test/` tree, a `Rakefile`, a `mocha.gemspec`, a `Gemfile`, a `gemfiles/` directory for testing against multiple dependency sets, a `.rubocop.yml` with a `.rubocop_todo.yml` for the existing offences, a `.yardopts` for the generated API documentation, and separate `docs/`, `CONTRIBUTING.md`, `RELEASE.md`, `MIT-LICENSE.md` and `COPYING.md` files.

The two licence files are worth a note. The project carries an MIT licence file and a separate COPYING.md, and GitHub's own classification of the repository licence is not one it can name, so if you are embedding this in a commercial product, read both files rather than assuming what the badge says.

On release cadence, the project publishes to RubyGems and has no GitHub releases at all, so the version history lives in the gem rather than in the repository tags. The last push to the repository was on 2026-09-22.

Editorial conclusion

Adopt mocha if your Ruby suite already uses Minitest, Test::Unit or RSpec and you want mock objects with expectation verification rather than a hand rolled stub helper, since the syntax is small and the framework integration is built in. Do not adopt it if you need fakes or test spies, because the project states that it is focused on mocks and stubs and that extending it that way is a deliberate non-goal for now. Verify first that the Mocha require happens after your test framework is loaded, since the documentation makes that ordering the rule rather than a suggestion, and that the licence files in the repository, an MIT-LICENSE.md and a COPYING.md, cover the way you intend to use the gem.

Frequently asked questions

How do I install and set up mocha for a Ruby test suite?

Install it with `gem install mocha`, then require the relevant integration file after your test framework is loaded: `require 'mocha/test_unit'` for Test::Unit or `require 'mocha/minitest'` for Minitest. With Bundler, add `gem 'mocha'` to the Gemfile and do the require later, for example at the bottom of `test_helper.rb`.

What is the difference between expects and stubs in mocha?

`expects` sets an expectation that the method is called, optionally with specific arguments via `with`, and the expectation is verified at the end of the test. `stubs` replaces the method with a return value and makes no demand about whether it was called.

Does mocha support fakes and test spies?

No, and that is deliberate. The project describes itself as a library for mocking and stubbing but deliberately not, yet, faking or spying, and says it has chosen to keep it focused on mocks and stubs even though extending it to fakes and spies would be possible.

How do I use mocha with RSpec and with Cucumber?

RSpec ships a mocha adapter, so you set `config.mock_with :mocha` in your spec helper and no require statement is needed. Cucumber needs the Mocha API mixed into the World and an around hook that calls `mocha_setup`, runs the scenario, calls `mocha_verify` and tears down in an ensure block.

Official sources

  1. freerange/mocha on GitHub
  2. Issues
  3. Project website
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/freerange-mocha.svg)](https://hysenlabs.com/projects/freerange-mocha)

Was this useful?