Posted by James Mead
Mon, 11 Sep 2006 02:59:00 GMT
Some recent converstations have made me realise that a lot of people are confused by the difference between mocking and stubbing. I’ve realised that I’ve only added to the confusion by calling part of the Mocha library, Stubba.
The difference between mocking and stubbing
Stubbing a method is all about replacing the method with code that returns a specified result (or perhaps raises a specified exception). Mocking a method is all about asserting that a method has been called (perhaps with particular parameters).
If you think about it, it’s difficult (or impossible?) to do mocking without stubbing – you need to return from the mocked method, so that the code under test can complete execution. So mocking libraries tend to implicitly or explicitly allow you to do stubbing.
Mocking and stubbing with Mocha
To stub a method, use the stubs method.
To mock a method, use the expects method.
The difference between Mocha and Stubba
Mocha is a traditional mocking library very much in the JMock mould. Stubba is a separate part of Mocha that allows mocking and stubbing of methods on real (non-mock) classes. It works by moving the method of interest to one side, adding a new stubbed version of the method which delegates to a traditional mock object. You can use this mock object to set up stubbed return values or set up expectations of methods to be called. After the test completes the stubbed version of the method is removed and replaced by the original.
I’m thinking of ditching the name Stubba, because this part of the library is not solely concerned with stubbing. Let me know what you think.
Martin Fowler has a must-read article on why Mocks Aren’t Stubs.
Another great reference is JMocks documentation – in particular Yoga for your Unit Tests.
Read more...
Tags difference, expectation, mocha, mock, mocking, ruby, stub, stubba, stubbing, testing | 3 comments
Posted by James Mead
Sun, 03 Sep 2006 18:20:00 GMT
Spurred on by Thorsten’s comment on Gluttonous’ blog and a mention of Mocha in the Top 5 Rails Stories of the Week, I’ve given the Mocha documentation a major overhaul.
Now the RDoc only shows the public API which should hopefully improve the signal-to-noise ratio and show how simple it is to use.
I’ve also had a play with
CodeRay and generated syntax-highlighted examples.
Good general information on mocking…
Tags coderay, highlight, jmock, mocha, mock, mocking, rails, ruby, stub, stubba, stubbing, syntax, test, testing | 4 comments
Posted by James Mead
Fri, 01 Sep 2006 06:26:00 GMT
I thought I’d compare and contrast the way Gluttonous was injecting mocks and how you can do it with Mocha.
Gluttonous’ way
def test_process_exit
delegate_methods_to_mock!(RailsFCGIHandler, :close_connection) do
fcgi = flexmock()
fcgi.should_receive(:close_connection)
@handler.mock = fcgi
@handler.stubs(:when_ready).returns(:exit)
@handler.process!
end
end
Using Mocha
def test_process_exit
@handler.expects(:close_connection)
@handler.stubs(:when_ready).returns(:exit)
@handler.process!
end
I think it’s a bit more readable and you don’t need the block construction which starts becoming a nuisance when you need to stub methods on multiple classes.
Tags fixture, mocha, mock, mocking, rails, ruby, stub, stubbing, test, testing | 4 comments
Posted by James Mead
Thu, 31 Aug 2006 04:25:00 GMT
It’s great to see that Gluttonous has been playing with Mocha. Like many people, he’s found the ability to mock or stub class methods particularly useful – and this is one of the key differentiators between Mocha and other Ruby mocking libraries.
He’s been trying to improve the test coverage for Rails and submitted this patch where there is an interesting discussion1 about why he would prefer to use Mocha. Interestingly, there has also been some recent discussion on the RSpec mailing list about adding Mocha-like functionality.
On a different note, in his article Mocks for Speed, Gluttonous draws attention to one of the advantages I have previously mentioned of using mocks extensively to write unit tests that test a class in isolation – namely a fast build.
1 We have now released Mocha under the MIT license so it can be used for testing within Rails.
Tags class, fixture, instance, method, mock, mocking, rails, ruby, stub, stubbing, test, testing | 4 comments
Posted by James Mead
Thu, 24 Aug 2006 17:59:00 GMT
A new version of the Mocha mocking and stubbing library developed at Reevoo has been released.
There is now a Ruby on Rails plugin which can be installed like this…
$ script/plugin install svn://rubyforge.org/var/svn/mocha/trunk
Here are the release notes…
- Rails plugin.
- Auto-verify for all expectations, including those on concrete classes.
- Include each expectation verification in the test result assertion count.
- Filter out noise from assertion backtraces.
- Point assertion backtrace to line where failing expectation was created.
- New yields method for expectations.
- Create stubs which stub all method calls.
- Mocks now respond_to? expected methods.
Thanks for patches from Chris.
Enjoy!
Posted in mocha_release | Tags expectation, fixtures, mock, mocking, plugin, rails, ruby, stub, stubbing, test, testing, verify | 14 comments
Posted by James Mead
Fri, 11 Aug 2006 11:57:00 GMT
I’ve just released a new version of the Ruby mocking and stubbing library, Mocha, that I first mentioned a while ago. Here are the release notes:
- Small change to SetupAndTeardown#teardown_stubs suggested by Luke Redpath to allow use of Stubba with RSpec.
- Reorganized directory structure and extracted addition of setup and teardown methods into SmartTestCase mini-library.
- Addition of auto-verify for Mocha (but not Stubba). This means there is more significance in the choice of expects or stubs in that any expects on a mock will automatically get verified.
So instead of…
wotsit = Mocha.new
wotsit.expects(:thingummy).with(5).returns(10)
doobrey = Doobrey.new(wotsit)
doobrey.hoojamaflip
wotsit.verify
you need to do…
wotsit = mock()
wotsit.expects(:thingummy).with(5).returns(10)
doobrey = Doobrey.new(wotsit)
doobrey.hoojamaflip
There are also shortcuts as follows…
instead of…
wotsit = Mocha.new
wotsit.expects(:thingummy).returns(10)
wotsit.expects(:summat).returns(25)
you can have…
wotsit = mock(:thingummy => 5, :summat => 25)
and instead of…
wotsit = Mocha.new
wotsit.stubs(:thingummy).returns(10)
wotsit.stubs(:summat).returns(25)
you can have…
wotsit = stub(:thingummy => 5, :summat => 25)
Posted in mocha_release | Tags mocha, mock, mocking, rails, ruby, stub, stubba, stubbing, test, testing | 4 comments
Posted by James Mead
Sun, 16 Jul 2006 19:01:00 GMT
Again – I’m not really very pleased with this example, but hopefully it makes some sense. It’s important to realise that the test is not running in a normal Rails environment with the standard auto-require. In fact the first time the Comment class is encountered AutoMocha uses const_missing to supply a Mocha::Mock in its place. From that point on – any further references get the same mock object.
class Article
attr_reader :id
def accepted_comments
Comment.find_all_by_article_id(self.id).select { |comment| comment.accepted? }
end
end
require 'rubygems'
require 'auto_mocha'
require 'test/unit'
class OrderTest < Test::Unit::TestCase
include Mocha
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
Tags class, dependency, expectation, method, mock, mocking, rails, ruby, stub, stubbing, test, testing | no comments
Posted by James Mead
Sun, 16 Jul 2006 19:00:00 GMT
I don’t really like this example, but it’s the best I’ve got at the moment. The any_instance bit is particularly contrived, but hopefully you get the idea.
class Order
attr_accessor :shipped_on
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
end
def number_shipped_since(date)
find_all.select { |order| order.shipped_on > date }.size
end
def unshipped_value
find_all.inject(0) { |total, order| order.shipped_on ? total : total + order.total_cost }
end
end
end
require 'rubygems'
require 'stubba'
require 'test/unit'
class OrderTest < Test::Unit::TestCase
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
def test_should_count_number_of_orders_shipped_after_specified_date
now = Time.now; week_in_secs = 7 * 24 * 60 * 60
order_1 = Order.new; order_1.shipped_on = now - 1 * week_in_secs
order_2 = Order.new; order_2.shipped_on = now - 3 * week_in_secs
Order.stubs(:find_all).returns([order_1, order_2])
assert_equal 1, Order.number_shipped_since(now - 2 * week_in_secs)
end
def test_should_calculate_value_of_unshipped_orders
Order.stubs(:find_all).returns([Order.new, Order.new, Order.new])
Order.any_instance.stubs(:shipped_on).returns(nil)
Order.any_instance.stubs(:total_cost).returns(10)
assert_equal 30, Order.unshipped_value
end
end
Tags class, expectation, method, mock, mocking, ruby, stub, stubbing, test, testing | no comments
Posted by James Mead
Sun, 16 Jul 2006 18:59:00 GMT
I thought I’d put the examples from the RDoc README up here if only for the syntax highlighting.
class Enterprise
def initialize(dilithium)
@dilithium = dilithium
end
def go(warp_factor)
warp_factor.times { @dilithium.nuke(:anti_matter) }
end
end
require 'rubygems'
require 'mocha'
require 'test/unit'
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
Tags expectation, instance, method, mock, mocking, ruby, stub, stubbing, test, testing | no comments
Posted by James Mead
Sun, 16 Jul 2006 17:51:00 GMT
While developing Mocha, I have to admit I’ve been somewhat remiss in keeping up-to-date with the latest developments on projects like RSpec and FlexMock. It looks like both projects have gone further than Mocha with implementing the JMock concepts. FlexMock also now supports simple class interception (see FlexMock::TestCase#intercept), but it admits to being “very simple-minded”, has “a number of restrictions” and “a proxy class constant will be left behind”.
I don’t think Stubba suffers from these limitations, but I’ll save a more detailed explanation for another post. It doesn’t look like either project has anything like AutoMocha.
Tags class, flexmock, interception, mock, mocking, rspec, stub, stubbing | no comments