Using OpenStruct to Enhance Your Mocks

So I use RSpec's built in mocking framework for my mocking/stubbing and it works quite well. I sent up a mock like so:

mock_active_record_instance =
mock("give it a name here, but probably a better one than this")

and put some expectations on it like so:

mock_active_record_instance.should_receive(:id).and_return(1)

but some calls to the mock I don't care about so I can stub them out like so:

mock_active_record_instance.stub!(:name).
and_return("Arthur, King of the Britons")

If I don't need it to return anything I can leave off the and_return and it will return nil. But it's tedious to do that. Lately I tend to use OpenStructs as my mocks like so:

mock_active_record_instance = OpenStruct.new
mock_active_record_instance.should_receive(:id).and_return(1)

Now here's what's cool about that: My spec(test) will fail if the expectation of a call to id isn't met, but any other calls will be ignored (and nil returned). If I had made mock_active_record_instance a mock, like in the first example, I'd have to mock or stub out all the calls it's going to get and that is just painful. This way I can ignore the stuff I don't want to spec(test) painlessly.

You can also do fun stuff like so:

mock_active_record_instance = OpenStruct.new(:height => "5'10''",
:weight => 260,
:cars => ["hyundai", "saturn"])


So now mock_active_record_instance.cars will return an array with 2 stings. And height will return "5'10''" and weight will return 260. Maybe, in the future, a call to weight will return a smaller number -- I'm working on it.

Warning -- it may be that you have to do this because too many things are going on in the method you are specifying(testing). A better solution might be to figure out how to shorten up that method.

Jay Fields wrote about using OpenStructs in a series of blog posts about stubbing in rails which are totally worth reading.

Comments

Anonymous said…
Jake,

I've found that using OpenStruct as a stub can quickly become messy. Tests that use it can begin to rely on the fact that OpenStruct can maintain state. I've had much better success with the stub_everything method in mocha. It behaves the same as an OpenStruct, but it always returns nil.

Jay
Dustin Tinney said…
I believe you can also say, object_mock = mock("some name" :null_object => true)
Jake Scruggs said…
Good point about :null_object, that can come in handy. But when I use that, I have to stub/mock out things individually on seperate lines. When I use an OpenStuct I can define the things I want to be stubbed out with return values and the rest of the calls just return nil. One stop shopping.

I'm not exactly sure what Jay is talking about with the state problems, but unfortunately Mocha doesn't currently play nice with RSpec.

Popular posts from this blog

What's a Good Flog Score?

Point Inside a Polygon in Ruby

SICP Wasn’t Written for You