magpiebrain

Sam Newman's site, a Consultant at ThoughtWorks

I’m taking a break from my recent series of articles on Ruby (see Parts one, two and three of Ruby for Java (and C#) programmers) to revisit the subject of mocking in Ruby. My recent overview of the currently available mocking tools lead me to discuss a better syntax for the next mocking API.

One of my aims when talking about a new syntax for a mocking API was in order to make expectations more readable – and more usable as a living document detailing how the code being tested actually works. As has already been discussed, Ruby’s flexible syntax makes it particularly suitable for the creation of Domain Specific Languages – and I think we can do much better than the existing API’s (or even the more mature JMock) in moving towards a DSL for mocking.

My original idea was to steal from the structure of JMock:

mockObject = Mock.new(ClazzToMock)
mockObject.expects(:someOperation).
  with(arg1, arg2).
  willReturn(someReturn)

To which Carlos (correctly) pointed out that I should make use of Ruby’s built-in language constructs – he proposed using hashes to avoid chaining calls together:

mock_obj = Mock.new(ClassToMock)
mock_obj.expects(
:do_stuff,
:with => [arg1, arg2],
:returns => someReturn
)

The obvious difference here is that the API cannot enforce the expectation to be written in a way that makes sense, as I could just as easily write:

mock_obj = Mock.new(ClassToMock)
mock_obj.expects(
:do_stuff,
:returns => someReturn,
:with => [arg1, arg2]
)

Also of note is that (when we get a decent one) IDE’s won’t be able to help you construct your expectations – this was part of the aims behind JMock’s structure.

Nat countered with the use of a mocking mixin, and using blocks as the expectations:

mock = Mock.new “exampleMock”
expect 1 { mock.do_something(eq(10), !null) }
expect 3..4 { mock.something_else(eq(“hello”) }

In EasyMock terminology, when you define the expectation in a block, it is in a record state – that is you are registering the calls that you expect to be made. In EasyMock, you then have to call replay before the mock can be used. This really doesn’t help readability (JMock gets around this by having a different object for recording expectations to the one used as the mock). With Nat’s approach, the mock is only in replay mode within an expect.

Taking this further, I cannot see why multiple expectations on multiple mocks couldn’t be defined inside a single expect call:

expect {
  mock1.halve(eq(10)).returns(5)
  mock1.double(eq(5)).returns(10)
  mock2.insult.returns("Boogers!")
}

Before (more importantly if) I decide work on this, I want to try and get some consensus of opinion as to what would make a sensible syntax, so feedback on Nat’s approach is appreciated!

About these ads

5 Responses to “Mocking with Ruby – a question of syntax”

  1. Stuart

    I’ve been having great fun in work recently playing around with the way we write tests, and as my colleague Mike said, developers spend more time reading code than writing it. Hence it makes sense to use the more readable approach to your mocking framework.

    Whilst I use JMock and find it by far the best mock object implementation out there… I still find it has rather a lot of fluff around it. Particularly when more than one method call is made to an object.

    With that said, I’d be tempted by the last syntax, except that I’d pull the mock object out hence:

    mock1.expect{
    halve(eq(10)).returns(5)
    }

    however, it may be made more readable by using something similar to your first effort:

    expect {
    :given => 10,
    :halve,
    :returns => 5
    }

    I’ve never coded Ruby, so forgive me if any of the syntax is incorrect, but I hope it kind of still makes sense.

    Reply
  2. Sam Newman

    The _syntax_ (as far as Ruby goes) is fine – the problem is the implementation. With your second example, how to you determine which mock is having the expectations defined on it? The idea of having the mock _inside_ the expectaion block is so you can then have a single expectation block for your test. If having such a block doesn’t help readability (or the implementation is too complex), then I’d favour your first example.

    Reply
  3. Nat

    I would prefer the returns stuff to be outside the block. E.g.:

    expect 1 { m.do_something() }.returns 10
    expect (at_least 1) { m.do_something_else() }.returns 20

    That makes the expected call really distinct from the action.

    I don’t care about typing expect more than once if the resulting code is easier to read.

    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 )

Twitter picture

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

Facebook photo

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

Google+ photo

You are commenting using your Google+ 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

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: