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:
and put some expectations on it like so:
but some calls to the mock I don't care about so I can stub them out like so:
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:
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:
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.
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
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
I'm not exactly sure what Jay is talking about with the state problems, but unfortunately Mocha doesn't currently play nice with RSpec.