magpiebrain

Sam Newman's site, a Consultant at ThoughtWorks

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”

  1. Carlos Villela

    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?

    Reply
  2. Sam Newman

    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).

    Reply
  3. Aslak Hellesoy

    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?

    Reply
  4. Sam Newman

    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()!

    Reply
  5. Nat Pryce

    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.

    Reply
  6. Sam Newman

    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)}

    Reply
  7. Nat Pryce

    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.

    Reply
  8. Jim Weirich

    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.

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Basic HTML is allowed. Your email address will not be published.

Subscribe to this comment feed via RSS

%d bloggers like this: