Posted by James Mead
Tue, 24 Jun 2008 19:14:00 GMT
There’s been quite a bit of work going on in Mocha over recent months, but a release is long overdue. The API is now pretty stable and so this release jumps from version 0.5 to 0.9. Much of the work has been refactoring Mocha’s internals to support new features and make the code more maintainable.
Before attempting the refactoring, extensive acceptance tests were added. One of the benefits of this is that it should now be easier to write new acceptance tests if you want to suggest new features or illustrate a bug ;-)
Here’s a quick summary of the changes in the release. I’ll try to post some code examples here in the near future.
Ordering constraints
Based on the JMock constraints with the same names…
Configurable warnings or errors
- When a method on a non-public method is stubbed
- When a method on a non-existent method is stubbed
- When a method on a non-mock object is stubbed (partial mocking)
- When a method is stubbed unnecessarily (i.e. the stubbed method is not called during the test)
See Configuration for more details.
Improved error messages
- A more readable and complete list of unsatisfied expectations, satisfied expectations and state machines.
- Display more sensible failure message for any_instance expectations.
Parameter matchers
- New to this release: optionally (allows matching of optional parameters if available), yaml_equivalent (allows matching of YAML that represents the specified object), responds_with (tests the quack not the duck).
- Nesting of matchers is now supported.
Syntax shortcut
An optional block can be passed into the Standalone#mock method. The block is evaluated in the context of the new mock instance and can be used as a shortcut to set up expectations.
Ruby & Rails compatibility
Tested with Ruby 1.8.4, 1.8.5, 1.8.6 & 1.9. All related bugs and warnings believed to be fixed.
Tested with Rails 1.2.3 & Rails 2.1.0.
Deprecation
There is no longer any need to have a “require ‘stubba’” statement in your code. A deprecation warning has been added to this effect, because the file will be removed in a future release.
Bug fixes
Posted in mocha_release | Tags jmock, mocha, mock, release, ruby, stub, testing | no comments
Posted by James Mead
Thu, 19 Jun 2008 20:32:38 GMT
The Mocha Mailing List has moved to Google Groups.
- Group name: mocha-developer
- Group home page: http://groups.google.com/group/mocha-developer
- Group email address mocha-developer@googlegroups.com
Tags google, group, mailman, mocha, mock, ruby | no comments
Posted by James Mead
Sun, 17 Feb 2008 18:10:01 GMT
Ola Bini one of the JRuby guys has released the JtestR tool which allows you to write tests for Java code in Ruby! Ola has bundled a number of Ruby libraries – Mocha, RSpec, Dust, Test::Unit & ActiveSupport – together with JRuby to allow you to write Ruby test cases that test Java code.
He has a couple of examples in the Mock documentation of how to use Mocha...
The first one demonstrates using Mocha to mock an interface (Map).
import java.util.Map
import java.util.Iterator
import java.util.Set
import java.util.HashMap
functional_tests do
test "that a new HashMap can be created based on another map" do
map = Map.new
map.expects(:size).returns(0)
iter = Iterator.new
iter.expects(:hasNext).returns(false)
set = Set.new
set.expects(:iterator).returns(iter)
map.expects(:entrySet).returns(set)
assert_equals 0, HashMap.new(map).size
end
end
The second example demonstrates using Mocha to setup expectations on a real (non-mock) instance (HashMap)...
import java.util.Iterator
import java.util.Set
import java.util.HashMap
functional_tests do
test "that a new HashMap can be created based on another map" do
map = mock(HashMap)
map.expects(:size).returns(0)
iter = Iterator.new
iter.expects(:hasNext).returns(false)
set = Set.new
set.expects(:iterator).returns(iter)
map.expects(:entrySet).returns(set)
assert_equals 0, HashMap.new(map).size
end
end
Tags java, mocha, mock, ruby, test | no comments
Posted by James Mead
Fri, 03 Aug 2007 10:10:00 GMT
Zach Moazeni has just posted a suggested patch for Mocha over on his blog. My understanding of the patch is that it means expectations are verified even when an assertion error occurs in the test. Here is his example…
class Car
def initialize(parts = [])
@parts = parts
end
def start
started = true
@parts.each do | part |
end
started
end
end
class SomeTest < Test::Unit::TestCase
def test_start
engine_mock = mock("engine_mock")
car = Car.new([engine_mock])
engine_mock.expects(:start).returns(false)
assert !car.start
end
end
I’ve had a friendly & useful conversation with Zach about it, but I’m not convinced this is the right way to go. Using the one assertion per test school of thought, you can achieve the same goal by splitting the test into two so you get a test failure for the expectation and another for the assertion…
class SomeTest < Test::Unit::TestCase
def test_should_start_engine
engine = mock('engine')
car = Car.new([engine])
engine.expects(:start)
car.start
end
def test_should_start_if_engine_starts
engine = stub('engine')
car = Car.new([engine])
engine.stubs(:start).returns(false)
assert !car.start
end
end
Something that makes the example less suitable for mocking is that the Car#start method is both a command and a query. If you separate the two, testing with mocks might be easier…
class Car
def initialize(parts = [])
@parts = parts
end
def start
@parts.each { |part| part.start }
end
def started?
@parts.all? { |part| part.started? }
end
end
class SomeOtherTest < Test::Unit::TestCase
def test_should_start_engine
engine = mock('engine')
car = Car.new([engine])
engine.expects(:start)
car.start
end
def test_should_not_be_started_if_engine_is_started
engine = stub('engine')
car = Car.new([engine])
engine.stubs(:started?).returns(false)
assert !car.started?
end
end
I’d be interested to know what other people think…
One thing I do agree with Zach about is that submitting a suggested patch to an open source project is a great way of initiating a constructive conversation.
Tags assert, command, expectation, mocha, mock, query, stub, testing, verify | 4 comments
Posted by James Mead
Sun, 22 Jul 2007 20:48:00 GMT
As promised I’ve finally got round to making the S5 slides of my LRUG talk available here.
You can use various keys to navigate the presentation. If you just want to quickly scan through the content, you might find the outline view useful (press the “T” key to toggle between slideshow & outline modes).
Tags lrug, mocha, mock, object, ruby, stub, testing | no comments
Posted by James Mead
Fri, 08 Jun 2007 15:46:00 GMT
or download one of the latest packages from rubyforge.
Parameter Matchers
I’ve added a few Hamcrest-style parameter matchers which are designed to be used inside Expectation#with. The following matchers are currently available: anything(), includes(), has_key(), has_value(), has_entry(), all_of() & any_of(). More to follow soon. The idea is eventually to get rid of the nasty parameter_block option on Expectation#with.
object = mock()
object.expects(:method).with(has_key('key_1'))
object.method('key_1' => 1, 'key_2' => 2)
object = mock()
object.expects(:method).with(has_key('key_1'))
object.method('key_2' => 2)
Values Returned and Exceptions Raised on Consecutive Invocations
Allow multiple calls to Expectation#returns and Expectation#raises to build up a sequence of responses to invocations on the mock. Added syntactic sugar method Expectation#then to allow more readable expectations.
object = mock()
object.stubs(:method).returns(1, 2).then.raises(Exception).then.returns(4)
object.method
object.method
object.method
object.method
Yields on Consecutive Invocations
Allow multiple calls to yields on single expectation to allow yield parameters to be specified for consecutive invocations.
object = mock()
object.stubs(:method).yields(1, 2).then.yields(3)
object.method { |*values| p values }
object.method { |*values| p values }
Multiple Yields on Single Invocation
Added Expectation#multiple_yields to allow a mocked or stubbed method to yield multiple times for a single invocation.
object = mock()
object.stubs(:method).multiple_yields([1, 2], [3])
object.method { |*values| p values }
Invocation Dispatch
Expectations were already being matched in reverse order i.e. the most recently defined one was being found first. This is still the case, but we now stop matching an expectation when its maximum number of expected invocations is reached. c.f. JMock v1. A stub will never stop matching by default. Hopefully this means we can soon get rid of the need to pass a Proc to Expectation#returns.
object = mock()
object.stubs(:method).returns(2)
object.expects(:method).once.returns(1)
object.method
object.method
object.method
This should still work…
Time.stubs(:now).returns(Time.parse('Mon Jan 01 00:00:00 UTC 2007'))
Time.now
Time.stubs(:now).returns(Time.parse('Thu Feb 01 00:00:00 UTC 2007'))
Time.now
Acknowledgements
Thanks to David Chelimsky, Dan North, Jay Fields, Kevin Clark, Frederick Cheung, James Moore, Brian Helmkamp, Ben Griffiths, Chris Roos & Paul Battley for their input. Apologies to anybody I forgot to mention.
Posted in mocha_release | Tags mocha, mock, release, ruby, stub, testing | 1 comment
Posted by James Mead
Thu, 10 May 2007 15:15:00 GMT
I just downloaded an updated version of the Pragmatic Bookshelf’s Agile Web Development with Rails book. Maybe it’s been there a while, but I just noticed Mocha is mentioned at the end of the chapter on testing. :-)
Tags agile, book, mocha, mock, pragmatic, stub | no comments
Posted by James Mead
Thu, 12 Apr 2007 11:57:00 GMT
I’ve finally managed to find some time to do some serious work on Mocha. Here are some code snippets showing the new functionality available in trunk (revision 128). I don’t don’t know how many people out there are using trunk, but it would be great to get some feedback on these changes before I make a new release. In particular I’d like to know whether…
- I’ve broken anybody’s tests.
- Anybody has a legitimate use for
parameter_block in Expectation#with that they don’t think will be handled by a suitable parameter matcher i.e. using the current behaviour where the block is passed the parameters and the result of the block determines whether the expectation matches. I’m planning on deprecating this soon.
- Anybody has a legitimate use for passing in an instance of
Proc to Expectation#returns i.e. using the current behaviour where the Proc gets executed to generate a return value. I’m planning on deprecating this soon as well.
I’ve typed this up in a bit of a rush (about to go on holiday for a few days), so apologies if there are any mistakes.
Parameter Matchers
I’ve added a few Hamcrest-style parameter matchers which are designed to be used inside Expectation#with. The following matchers are currently available: has_key(), has_value() & has_entry(). More to follow soon. The idea is eventually to get rid of the nasty parameter_block option on Expectation#with.
object = mock()
object.expects(:method).with(has_key('key_1'))
object.method('key_1' => 1, 'key_2' => 2)
object = mock()
object.expects(:method).with(has_key('key_1'))
object.method('key_2' => 2)
Values Returned and Exceptions Raised on Consecutive Invocations
Allow multiple calls to Expectation#returns and Expectation#raises to build up a sequence of responses to invocations on the mock. Added syntactic sugar method Expectation#then to allow more readable expectations.
object = mock()
object.stubs(:method).returns(1, 2).then.raises(Exception).then.returns(4)
object.method
object.method
object.method
object.method
Yields on Consecutive Invocations
Allow multiple calls to yields on single expectation to allow yield parameters to be specified for consecutive invocations.
object = mock()
object.stubs(:method).yields(1, 2).then.yields(3)
object.method { |*values| p values }
object.method { |*values| p values }
Multiple Yields on Single Invocation
Added Expectation#multiple_yields to allow a mocked or stubbed method to yield multiple times for a single invocation.
object = mock()
object.stubs(:method).multiple_yields([1, 2], [3])
object.method { |*values| p values }
Invocation Dispatch
Expectations were already being matched in reverse order i.e. the most recently defined one was being found first. This is still the case, but we now stop matching an expectation when its maximum number of expected invocations is reached. c.f. JMock v1. A stub will never stop matching by default. Hopefully this means we can soon get rid of the need to pass a Proc to Expectation#returns.
object = mock()
object.stubs(:method).returns(2)
object.expects(:method).once.returns(1)
object.method
object.method
object.method
This should still work…
Time.stubs(:now).returns(Time.parse('Mon Jan 01 00:00:00 UTC 2007'))
Time.now
Time.stubs(:now).returns(Time.parse('Thu Feb 01 00:00:00 UTC 2007'))
Time.now
Acknowledgements
Thanks to David Chelimsky, Dan North, Jay Fields, Kevin Clark, Frederick Cheung, James Moore, Brian Helmkamp, Ben Griffiths, Chris Roos & Paul Battley for their input. Apologies to anybody I forgot to mention.
Tags mocha, mock, preview, raise, return, ruby, stub, yield | 4 comments
Posted by James Mead
Thu, 05 Apr 2007 12:59:00 GMT
We have our own home-grown continuous integration application at work, but a couple of weeks ago, I decided to have a go at getting CruiseControl.rb up and running at home for Mocha. It all went pretty smoothly and I’m happy with the result. Thanks are due to the CruiseControl.rb team for clear instructions and a simple-to-use application.
I’d like to publish the url for my CruiseControl instance, but I’m concerned about random punters clicking the build button and loading my already creaking server, so a screenshot will have to suffice for now…
It would be nice if you only saw the build button if you were logged in. I feel a feature request coming on…
On a related note, it’s great to see that there has been some effort going into getting a RubyOnRails CruiseControl.rb instance set up.
Tags control, cruise, mocha, ruby, thoughtworks | 5 comments
Posted by James Mead
Wed, 04 Apr 2007 16:11:00 GMT
The RSpec team have just announced a beta version of their pending 0.9 release.
Apparently…
You can now use Mocha by saying
in a
Sounds good :-)
Tags bdd, compatibility, mocha, mock, rspec, testing | 2 comments