Mocha

Mocha is a library for mocking and stubbing with unit tests using a syntax like that of JMock and SchMock.

Mocha comes in three parts:

1. Mocha - traditional mock objects with expectations and verification
2. Stubba - allows mocking and stubbing of methods on real (non-mock) classes
3. AutoMocha - magically provides mocks in the place of undefined classes

Stubba & AutoMocha are the main difference between this mocking library and others like FlexMock and RSpec.

Provenance

Mocha & Stubba have been created by amalgamating a number of techniques developed by my Reevoo colleagues (Ben, Chris & Paul) and I under a common syntax. They are both in use on real-world projects using Ruby On Rails. AutoMocha is more experimental and is at an earlier stage of development.

Download & Installation

You can download Mocha from here or install Mocha with the following command.

$ gem install flexmock

License

Copyright Revieworld Ltd. 2006


You may use, copy and redistribute this library under the same terms as Ruby itself (see www.ruby-lang.org/en/LICENSE.txt).

Simple Mocha Example

class Enterprise

  def initialize(dilithium)
    @dilithium = dilithium
  end

  def go(warp_factor)
    warp_factor.times { @dilithium.nuke(:anti_matter) }
  end

end

require 'mocha'

class EnterpriseTest < Test::Unit::TestCase

  include Mocha

  def test_should_boldly_go
    dilithium = Mock.new
    dilithium.expects(:nuke).with(:anti_matter).at_least_once
    enterprise = Enterprise.new(dilithium)
    enterprise.go(2)
    dilithium.verify
  end

end

Simple Stubba Example

class Order

  def total_cost
    line_items.inject(0) { |total, line_item| total + line_item.price } + shipping_cost
  end

  def total_weight
    line_items.inject(0) { |total, line_item| total + line_item.weight }
  end

  def shipping_cost
    total_weight * 5 + 10
  end

  class << self

def find_all Database.connection.select_all(‘select * from orders’) end

def number_shipped_since(date) find_all.select { |order| order.shipped_on > date }.size end def unshipped_value find_all.inject(0) { |order| order.shipped_on ? 0 : order.total_cost } end end

end

require 'stubba'

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

  # illustrates stubbing class method
  def test_should_count_number_of_orders_shipped_after_specified_date
    order_1 = Order.new(:shipped_on => 1.week.ago)
    order_2 = Order.new(:shipped_on => 3.weeks.ago)
    Order.stubs(:find_all).returns([order_1, order_2])
    assert_equal 1, Order.number_shipped_since(2.weeks.ago)
  end

  # illustrates stubbing instance method for all instances of a class
  def test_should_calculate_value_of_unshipped_orders
    order_1 = Order.create
    order_2 = Order.create
    Order.any_instance.stubs(:shipped_on).returns(Time.now)
    Order.any_instance.stubs(:total_cost).returns(10)
    assert_equal 20, Order.unshipped_value
  end

end

Simple AutoMocha Example

class Article

  def accepted_comments
    Comment.find_all_by_article_id(self.id).select { |comment| comment.accepted? }
  end

end

require 'auto_mocha'

class OrderTest < Test::Unit::TestCase

  include Mocha

  # illustrates stubbing of previously undefined class Comment
  def test_should_return_accepted_comments_for_this_article
    unaccepted_comment = Mock.new(:accepted? => false)
    accepted_comment = Mock.new(:accepted? => true)
    comments = [unaccepted_comment, accepted_comment]
    Comment.stubs(:find_all_by_article_id).returns(comments)
    article = Article.new
    assert_equal [accepted_comment], article.accepted_comments
  end

end