Uh, ya, another great blog title, but we'll get over it. We use geocoding in our app, and that's a relatively costly operation time wise, especially when you may be doing it hundreds or thousands of times when your test suite runs. I can't stub out the objects that use it in many cases, so I wanted to stub out the actual geocode
call unless I truly needed real geocoding (which is only when I'm testing the actual geocoding itself, and thus is a very small part of the test suite).
We use RSpec Specs and Stories and I wanted to mock out the geocoding by default, but unmock it in a few places. I asked about this on the mailing list, Googled and so on, but didn't find a solution that was working. So here is what I wound up doing...
In my spec_helper.rb
file, I added:
Spec::Runner.configure do |config|
config.before(:each) do
# Setup fake geocoding unless told not to
unless @do_not_mock_geocoding
fake_geocode = OpenStruct.new(:lat => 123.456, :lng => 123.456, :success => true)
GeoKit::Geocoders::MultiGeocoder.stub!(:geocode).and_return(fake_geocode)
end
end
end
What this does is mock the geocoding unless a test has set the
@do_not_mock_geocoding
variable to true
. One caveat, at least from what I've found, is that you need to set that to true in a before(:all)
block in your tests, so that it happens before the before(:each)
. This is minor, as you can just have something like:
describe "with real geocoding" do
before(:all) do
@do_not_mock_geocoding = true
end
# your tests that want real geocoding
end
The impact this has had on our test suite is tremendous. I had already had some partial mocking of the geocoding in place, but was sweeping the system to put it in because the time it took to run our test suite was out of hand at about 13 minutes! Now that I've got this in, it runs in 2 minutes! Geocoding is used in two of our most core objects, which is why it has such a big impact on the test suite. This is one place mocking has really proved to be a massive value!