Posted by James Mead
Sat, 30 Aug 2008 16:48:00 GMT
Release Notes
- Fixed bug #21465 – expects & stubs should support method names as strings (as well as symbols) or fail fast. Convert all expectation method names to a symbol in case they were supplied as a string.
- By removing Mock#unexpected_method_called we reduce the number of methods vulnerable to the problem that surfaced in bug #21563.
- Fix bug #21563 – stubbing ‘verified?’ method is unsafe. Instance method names on the Mock class should be more obscure.
- Performance improvement. StubbaExampleTest goes twice as fast on my local machine.
- Added primitive performance test to default rake task.
- Fix format of case statements which don’t work in Ruby 1.9 and make others consistent.
- There is no point in running (potentially expensive) checks if configuration is set to allow such checks to fail. This is a relatively quick fix in response to Chris McGrath’s performance problems.
- Fix for bug #21161 – ‘uninitialized constant Deprecation in stubba.rb’.
- It’s more readable to talk about ‘once’ and ‘twice’ rather than ‘1 time’ and ‘2 times’.
- Fix bug #20883 – never should raise when called to prevent follow up errors. Fail fast when there are no matching invokable expectations and handle the stub_everything case sensibly. This might not be entirely backwards compatible, but I think the benefits outweigh the risks. The most likely change is that a test that was already failing will now fail faster, which doesn’t seem so awful.
Posted in mocha_release | Tags mocha, mock, ruby, stub, tdd, testing | no comments
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
Sat, 05 Apr 2008 00:10:06 GMT
Introduction
Some time ago, while I was pair-programming with him, Chris alerted me to a Ruby bug he’d come across which was interfering with the diagnosis of a bug in our application. Since then I’ve tried to find out more about it, but couldn’t find much, so I’ve done a bit of investigation and thought I’d post it here in case it’s useful to anyone else. The bug has long since been fixed, but I’m sure there are still people our there with the affected versions of Ruby 1.8.6.
Ruby bug
As far as I understand it, the bug is in Ruby’s Kernel.at_exit hook. A call to Kernel.exit(false) should cause the process to exit with an exit status of 1 indicating the process did not complete successfully. The bug means that calling Kernel.exit(false) from within Kernel.at_exit incorrectly causes the process to exit with an exit status of 0.
The most relevant bug report is #9300 and the most relevant mailing list thread is made up of:- [ruby-core:10746], [ruby-core:10748], [ruby-core:10760].
The fix seems to be in changeset 12126…
r12126 | nobu | 2007-03-23 16:53:42 +0000 (Fri, 23 Mar 2007) | 9 lines
* eval.c (ruby_cleanup): exit by SystemExit and SignalException in END
block. [ruby-core:10609]
* test/ruby/test_beginendblock.rb (test_should_propagate_exit_code):
test for exit in END block. [ruby-core:10760]
* test/ruby/test_beginendblock.rb (test_should_propagate_signaled):
test for signal in END block.
Implications for Test::Unit & Rake::TestTask
The bug has some important consequences. Test::Unit makes use of this mechanism to report test failures. Unfortunately, the bug means that a Test::Unit process will always return an exit status of 0 even when there have been test failures.
From
test/unit.rb:
at_exit do
unless $! || Test::Unit.run?
exit Test::Unit::AutoRunner.run
end
end
This in turn means that a Rake::TestTask process will also always return an exit status of 0 even when there have been test failures. This is significant, because many continuous integration systems rely on Rake::TestTask processes returning an exit status of 1 to indicate that there have been test failures. Thus you will get false positive passing builds – not good.
Affected versions of Ruby
I’ve built and installed a number of versions of Ruby and run tests on them to try to establish which ones are affected. Although they aren’t comprehensive, here are the results…
| affected? |
version |
N |
ruby 1.8.4 (2005-12-24) [i686-darwin8.10.3] |
N |
ruby 1.8.5 (2006-08-25) [i686-darwin8.10.3] |
N |
ruby 1.8.5 (2007-03-16 patchlevel 37) [i686-darwin8.10.3] |
N |
ruby 1.8.5 (2008-03-03 patchlevel 115) [i686-darwin8.10.3] |
Y |
ruby 1.8.6 (2007-02-17 patchlevel 0) [i686-darwin8.10.3] |
Y |
ruby 1.8.6 (2007-03-13 patchlevel 0) [i686-darwin8.10.3] |
Y |
ruby 1.8.6 (2007-03-16 patchlevel 2) [i686-darwin8.10.3] |
Y |
ruby 1.8.6 (2007-03-19 patchlevel 4) [i686-darwin8.10.3] |
Y |
ruby 1.8.6 (2007-05-22 patchlevel 5) [i686-darwin8.10.3] |
Y |
ruby 1.8.6 (2007-05-22 patchlevel 6) [i686-darwin8.10.3] |
Y |
ruby 1.8.6 (2007-05-22 patchlevel 7) [i686-darwin8.10.3] |
N |
ruby 1.8.6 (2007-05-22 patchlevel 8) [i686-darwin8.10.3] |
N |
ruby 1.8.6 (2007-05-23 patchlevel 9) [i686-darwin8.10.3] |
N |
ruby 1.8.6 (2007-05-23 patchlevel 10) [i686-darwin8.10.3] |
N |
ruby 1.8.6 (2007-08-22 patchlevel 50) [i686-darwin8.10.3] |
N |
ruby 1.9.0 (2007-11-28 patchlevel 0) [i686-darwin8.10.3] |
Tags bug, code, continuous, exit, hook, integration, ruby, test, testing, unit | no comments
Posted by James Mead
Mon, 17 Mar 2008 09:23:26 GMT
Not long after Ben persuaded me to join the fledgling Reevoo, we got our first big ReevooMark partners (Dixons & Currys) live. To celebrate this event and my having left Java behind at Thoughtworks, Ben bought me a Java Rehabilitation Clinic mug. The mug has recently developed a crack. I wonder if it’s trying to tell me it’s time to learn another language…

Java Rehabilitation Clinic mug
Tags clinic, java, rehab, ruby, thoughtworks | 1 comment
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
Thu, 29 Nov 2007 12:17:48 GMT
A few months back, in my Introduction to Mock Objects talk at LRUG, I talked about “Mock Object Injection”. At the time I described a number of different ways of replacing a production object with a Mock Object using Mocha. I remember that at the meeting, James Adam (who has since joined the team at Reevoo) asked me why I didn’t like the Any Instance Stub Injection technique.
I’m not sure I gave him a very convincing response and I’ve been meaning for ages to have a better go at explaining what I think are the pros and cons of each of the techniques I mentioned. Here’s the list of techniques with the ones I like best at the top. I still haven’t done a very good job, but I’d be interested to hear what other people think so that I can try and improve my understanding.
Constructor Injection
The ClassUnderTest allows its dependencies to be passed in as parameters to its constructor. A mock object is passed in as a replacement for the “real” collaborator. It may be convenient to specify the production collaborator as a default parameter value.
Advantages
The dependencies of the ClassUnderTest are explicit.
Disadvantages
Can’t think of any at the moment.
class ClassUnderTest
def initialize(dependency = Collaborator.new)
@dependency = dependency
end
def do_something
end
end
collaborator = mock('collaborator')
instance_under_test = ClassUnderTest.new(collaborator)
instance_under_test.do_something
Parameter Injection
The ClassUnderTest allows its dependencies to be passed in as parameters to the method under test. A mock object is passed in as a replacement for the “real” collaborator. It may be convenient to specify the production collaborator as a default parameter value.
Advantages
The dependencies of the method under test are explicit.
Disadvantages
Can’t think of any at the moment.
class ClassUnderTest
def do_something(local_dependency = Collaborator.new)
end
end
collaborator = mock('collaborator')
instance_under_test = ClassUnderTest.new
instance_under_test.do_something(collaborator)
Stubbed New Method Injection
Use Mocha’s Object#stubs to temporarily replace Collaborator#new with a stub implementation that returns a mock object.
Advantages
Better than Any Instance Stub Injection, because you can have more control over different instances of Collaborator.
Disadvantages
Dependencies of the ClassUnderTest are hidden and not explicit.
class ClassUnderTest
def initialize
@dependency = Collaborator.new
end
def do_something
end
end
collaborator = mock('collaborator')
Collaborator.stubs(:new).returns(collaborator)
instance_under_test = ClassUnderTest.new
instance_under_test.do_something
Writer Method Injection
Use an attribute writer method to replace the “real” collaborator with a mock object.
Disadvantages
The ClassUnderTest has to unnecessarily expose a way to modify its internal state. The test is coupled to the implementation of the ClassUnderTest.
class ClassUnderTest
attr_writer :dependency
def initialize
@dependency = Collaborator.new
end
def do_something
end
end
collaborator = mock('collaborator')
instance_under_test = ClassUnderTest.new
instance_under_test.dependency = collaborator
instance_under_test.do_something
Stubbed Private Method Injection
Use partial mocking to temporarily replace a private builder method with a stubbed version of the method.
Disadvantages
The test is coupled to the implementation of the ClassUnderTest. The partial mocking of the instance_under_test means that the test is not testing a pristine instance of the ClassUnderTest, but a modified one. It also means that the boundaries between test code and production code are less clear.
class ClassUnderTest
def do_something
local_dependency = build_collaborator()
end
private
def build_collaborator
Collaborator.new
end
end
collaborator = mock('collaborator')
instance_under_test = ClassUnderTest.new
instance_under_test.stubs(:build_collaborator).returns(collaborator)
instance_under_test.do_something
Any Instance Stub Injection
Use Mocha’s Class#any_instance method to temporarily replace the method on a collaborator with a stub method.
Disadvantages
The stubbed method is applied to all instances of the collaborating class. If the instance_under_test interacts with the stubbed method on more than one instance of the collaborating class, it isn’t possible to specify different behaviour for the stubbed method on each instance. Even if the instance_under_test only interacts with the stubbed method on one instance of the collaborating class, the test is specifying more stubbed behaviour than strictly necessary which could lead to false positives.
class ClassUnderTest
def do_something
local_dependency = Collaborator.new
return local_dependency.do_stuff
end
end
Collaborator.any_instance.stubs(:do_stuff).return('something useful')
instance_under_test = ClassUnderTest.new
instance_under_test.do_something
Instance Variable Set Injection
Use Object#instance_variable_set to replace the reference to a collaborator with a mock object.
Disadvantages
The test is coupled to the implementation of the ClassUnderTest. In particular the test is coupled to the supposedly private instance variable. In my opinion, it would be more honest to expose the instance variable by adding an attribute writer and using Writer Method Injection.
class ClassUnderTest
def initialize
@dependency = Collaborator.new
end
def do_something
end
end
collaborator = mock('collaborator')
instance_under_test = ClassUnderTest.new
instance_under_test.instance_variable_set(:@dependency, collaborator)
instance_under_test.do_something
Tags collaborator, dependency, injection, mock, object, ruby, testing | 1 comment
Posted by James Mead
Tue, 07 Aug 2007 17:05:00 GMT
Recently Luke and I spotted a subtle bug in our code where we were mistakenly assuming a method returned an Array when it actually returned a Fixnum. We were then calling the #size method on the result, but interestingly Fixnum#size returns the number of bytes in its machine representation.
Previously I had noticed that my colleague Paul Battley tended to use Array#length in preference to Array#size. Now I know why, or at least I know why I’m going to start using Array#length! If we’d used #length in this case we would have seen a NoMethodError instead of the spurious value, 4.
[5,6].size
1.size
[5,6].length
1.length
Update: Josh Susser has posted a useful related article about the use of…
Tags array, bug, length, machine, representation, ruby, size | 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