{ |one, step, back| } 1 of 1 article Syndicate: full/short

What Should flexmock(real_obj) Return?   22 May 07
[ print link all ]

Bruce Williams asked this question at RailsConf, and I am soliciting feedback.

Background

First, a little background. There are two possibilities when calling flexmock(). First, you are creating a full mock object:

# Example 1
mock = flexmock("description")
mock.should_receive(...)

A full mock fulfills two roles: (1) it is a target for should_receive to define expectations, and (2) it is a target for normal domain messages when testing.

The other possibility is that you are creating a partial mock (i.e. a regular Ruby object with just a few mocked methods):

# Example 2
real_obj = RealObject.new
proxy_mock = flexmock(real_obj, "description")
proxy_mock.should_receive(...)

The object returned from the flexmock() method is actually a proxy object that can accept should_receive() messages to define the expectations, but does not handle normal domain messages. After all, we have a real object that that handles domain messages.

Partial Mocks

It is clear that when creating a partial mock using the non-block form of flexmock(real_obj), we must return the proxy, else there would be no way to add expectations. But the return value for the block form of flexmock is not so clear.

Consider the following code:

# Example 3
real_obj = RealObject.new
result = flexmock(real_obj) do |mock|
  mock.should_receive(...)
end

Here the proxy object is passed as the block argument. All the expectation setup is done within the block. It is very tempting to write this code as:

# Example 4
result = flexmock(RealObject.new) do |mock|
  mock.should_receive(...)
end

But here is the problem: in example 4 we no longer have a reference to the RealObject instance. The flexmock() method returns the proxy object, not the real object; just as it does in the non-block form.

Bruce’s Suggestions

Bruce suggested changing the block version of flexmock() to go ahead and return the real object. Since the proxy is used in the block, there is no real need for it outside the function. And, I will admit, example 4 is short and relatively clear, especially with those familiar with the returning idiom used in Rails.

The Dilemma

So here is my dilemma. Changing FlexMock so that example 4 works properly is attractive. And I suspect that the return value of flexmock(real_obj) is not ever used in a significant way in existing code, so backwards compatibility should be be only a minor concern. However, changing the return object based on whether or not the method has a block just seems … wrong.

There is precedent for this. In the standard Ruby libraries open(fn) and open(fn) { ... } return different things (an open file for the former and the value of the block for the latter). I’ve never had problems with this behavior in open, so perhaps I am just being over sensitive here.

I told Bruce I would blog the issue and consider the feedback received. So let me know what you think. Should flexmock() be modified to return the real_object when defining partial mocks using the block form?

You can email me (jim@weirichhouse.org) or add a comment using the comments link below.


blog comments powered by Disqus

 

Formatted: 09-Feb-10 03:36
Feedback: jim@weirichhouse.org