I’ve been writing a ‘toy’ Rails application by way of a learning excerise (read: attempting to de-hype Rails, in my own mind if nowhere else). Whilst investigating at the tool offerings (or lack thereof) I started looking at the mocking tools available. Coming from a perspective as a frequent JMock user I was hoping to find something similar.
<!-more->
RubyMock
Originally written way back in 2001 by Nat Pryce (who also wrote Java’s DynaMock and JMock tools) RubyMock was the first mocking tool for Ruby. It was written to integrate with RubyTest which has now been superceeded by TestUnit – now built in to the core Ruby distribution.
Test::Unit::Mock
Test::Unit::Mock
(try saying that three times fast) is a reimplementation of RubyMock, only this time using the newer TestUnit. For someone used to the nice, readable JMock expectations, RubyMock is far from pretty. This example shows setting the expectation for a mock to return a value when the do_stuff
method is called:
(C)
(C)<textarea name="code" class="ruby" cols="60" rows="10">
mock_object.setReturnValues( :do_stuff => [
“Mocked Return”
] )
</textarea>
Personally, I dont find it as readable as JMock:
mock_object. expects(once()). method("do_stuff"). will(returnValue("Mocked Return"))
That seems about as sophisticated as the mocks get – you can specify call order (something I’ve never needed to do in JMock) but I couldn’t see any way to verify expected arguments made in method calls, apart from adding your own mocking methods.
FlexMock
FlexMock seems a little more recent than either of the above, and I prefer it’s wording if nothing else:
mock_object = FlexMock.new mock_object.mock_handle(:do_stuff) { "Mocked Return" }
The only way to assert arguments being passed to the mock is to place assertions within the block itself – compare this with JMock:
mock_object.expects(once()). method("do_stuff"). with(eq(someParam)). will(returnValue("Mocked Return"))
A JMock port for Ruby?
Much of JMock is focused at making readable expectations – I certainly find them to be more self-explanatory than the Ruby equivilents shown above. There is a certain amount of extra language required for the expectations to work in Java which wouldn’t need to be done for Ruby – for example you could do away with the need to cast the mock’s proxy, so instead or:
MyObject object = new MyObject((Dependency)mockDependency.proxy());
You could have:
MyObject object = MyObject.new(mockDependency.proxy())
Even better, you could do away with the need for the proxy altogether due to the nature of Ruby’s typing:
MyObject object = MyObject.new(mockDependency)
Likewise I think there is scope to simplify the way you define expectations in JMock. Instead of:
Mock mockObject = new Mock(ClazzToMock.class); mockObject.expects(once()). method("someOperation"). with(eq(arg1), eq(arg2)). will(returnValue(someReturn));
Compared to:
mockObject = Mock.new(ClazzToMock) mockObject.expects(:someOperation). with(arg1, arg2). willReturn(someReturn)
Implementing such a mocking tool would be simple – much of the work in JMock covers the creation and use of dynamic proxies, which are a breeze to implement in Ruby. Whilst writing my tool I’ve already created the bits that should make this possible, the question is whether or not I can be bothered to finish it off. In the meantime I’m going to take a look at the Python version of JMock, PMock for inspiration.
10 Responses to “Mocking Ruby”
That’d be a great addition to the Ruby toolset, Sam. I’d like to recommend using hashes and named parameters instead of chained method calls:
mock_obj = Mock.new(ClassToMock)
mock_obj.expects(
:do_stuff,
:with => [arg1, arg2],
:returns => someReturn
)
Kinda looks more like Ruby to me, but may go against some other JMock design principle I’m not aware of. What do you think?
Hmm – I’ll have to think about that. I suppose it makes sense – the method name will be there all the time expect when stubbing, but both the argument list and the return values are optional (there would also need to be a :throws ) of course.
Much of the design behind JMock was there not only to make the expectations readable – which your code certainly is – but also to help the IDE create expectations using autocompletion (something that us Ruby programmers won’t be having for a while I think).
Since ruby uses duck typing, creating a mock shouldn’t require passing a class to the mock object’s constructor.
Further, Ruby’s method_missing wouldn’t require you to have a proxy method on the mock to get the mocked instance. It can be the same object, thereby vastly improving its ease of use.
This would require that the mock’s “setup” methods don’t clash with the mocked methods. This can be achieved with a naming convention, like prefixing “setup” methods with ‘__’.
This is the approach used in MockIt, a very small, but powerful mock library written by Jon Tirsen. You can find it here:
http://svn.damagecontrol.codehaus.org/trunk/rscm/test/rscm/mockit.rb
http://svn.damagecontrol.codehaus.org/trunk/rscm/test/rscm/mockit_test.rb
This mock implementation also hooks into Test::Unit’s teardown in a similar way to JMock, so that mock.verify is not needed. This requires you to use include the MockIt module (mixin) in your test and use the new_mock method to create a mock. Example:
http://svn.damagecontrol.codehaus.org/trunk/damagecontrol/test/damagecontrol/project_test.rb?rev=1458&view=auto
MockIt doesn’t have the JMock feel that would allow you to use structures like once(), but its usage of blocks for constraints makes it very rubyish. Example:
foo = new_mock
foo.__expects(:bar).do |param_1, param_2|
assert_equal(“hi”, param_1)
assert_equal(“there”, param_1)
“yo”
end
assert_equal(“yo”, foo.bar(“hi”, “there”))
This works really well for me. Imagine that we add support for JMock style invocation counts and possibly shorthand parameter constraints and invocation results such as:
foo = new_mock
foo.__expects(once(:bar)).with(eq(“hi”), eq(“there”)).will(return_value(“bar”))
assert_equal(“yo”, foo.bar(“hi”, “there”))
I’m not sure it would add that much to the readability. You get very far with blocks in Ruby and MockIt. I admit adding support for invocation counts such as once() could be implemented w/o sacrificing readability, but I rarely use other count qualifiers than once() anyway.
WDYT?
Firstly, the reason why I’d pass a class in to be mocked is so you can differentiate between methods which the object should be able to handle, and those it shouldn’t – so throwing the normal method missing error.
Secondly I agree you don’t need a proxy method to use method missing – the mock and the proxy can be the same thing. I was showing the .proxy call by way of showing a gradual move from JMock syntax/construct, to a more Ruby way of looking at it.
I have a problem with how messy Ruby syntax can end up looking, so seeing things like double underscores kind of makes me want to vomit. Sure, one you understand it it’s easy to read, but before then it’s a further barrier to entry (with lack of tool support, there are already enough of those).
MockIt is not readable to me at all, certainly not compared to JMock. Remember that the original idea was that JMock should read like a “proper sentence”:http://nat.truemesh.com/archives/000188.html but the Java syntax got in the way – with Ruby needing less in the way of brackets I think there is a chance of making the expectations _more_ readable, not _less_.
I do agree about once() though – I’ve never found a need to use twice()!
I have some really cool ideas about how to do Mocks in Ruby. Here’s what it would look like:
mock = Mock.new “exampleMock”
expect 1 { mock.do_something(eq(10), !null) }
expect 3..4 { mock.something_else(eq(“hello”) }
… etc. etc…
I’ll discuss how to do this on Saturday if you want.
That looks good – but why the need for the eq() call? And discussing this saturday would be good…
To be clear about my query concerning eq(), I think the equals constraint should be the default if possible. As for the number of times you expect the method to be called, I rarely use it but agree with Ade that in some cases it’s useful. How about overloading expect to provide two forms, one assuming that it will be called once, so you can have:
expect {mock.do_something(10)}
expect {mock.do_something_else(10), 42}
expect 3..4 {mock.do_something(10)}
Why the eq call? Because I’m a duck-typing nazi!
How can the mock tell if an argument is a constraint or a normal object? It can’t without checking it’s class hierarchy or callnig respond_to? on it.
Looking at the class hierarchy is bogus in a dynamically typed language: the class hierarchy defines how the object is *implemented* and has no relation to its type. The type is defined solely by how the object responds to messages. So, it’s perfectly valid to define a constraint object by writing an object that has matches? and describe_to methods (or whatever they’d be in Ruby).
Using respond_to is equally bogus. I might use the method names matches? and describe_to? in other, unrelated, classes and want to set up expectations in which those objects are expected arguments. If the mock looked at the methods, it would incorrectly think that they were constraints, when they should instead be *wrapped* by constraints.
You may think that this is a bit academic, but I’ve been bitten by both problems when using Ruby libraries in real projects.
In a dynamically typed language you sometimes have to be more explicit about what you are passing around than you would in a statically typed language. That’s not a bad thing; it makes the code easier to read.
Regarding eq() … you can still ducktype without using an explicit eq() call. Implement your constraint objects to respond to a mock_constraint method that just returns self. Then in Kernel, define:
def mock_constraint
eq(self)
end
No need for respond_to?, no need for explicit class checking. The only concern is to make the mock_constraint command suitably unique to avoid name clashes.
Have you seen Mocha?