I've played around a bit on this. The constant setting with the service
locator pattern looked good, but I couldn't get the undoing/resetting of the
constant to work properly, not sure what I was doing wrong there.
This appears to be a solution though:
In my spec_helper.rb (which is required by all specs), I put this:
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
Then, in tests where I want real geocoding, I just
set @do_not_mock_geocoding to true.
On Fri, Sep 5, 2008 at 8:36 PM, Pat Maddox <[EMAIL PROTECTED]> wrote:
> On Fri, Sep 5, 2008 at 2:15 PM, Christopher Bailey <[EMAIL PROTECTED]>
> wrote:
> > I use geocoding in our app, and it permeates most of the core
> functionality.
> > Because it makes a call out to Google or Yahoo or what not to do the
> > geocoding, I'd like to mock this for the bulk of my tests, except for the
> > few tests that actually do stuff where they need the real data. I had
> > started wrapping all my specs with the equivalent (but a DRY form) of:
> > GeoInfo = Struct.new(:lat, :lng, :success)
> > describe "with fake geocoding" do
> > before(:all) do
> > fake_geocode = GeoInfo.new(123.456, 789.012, true)
> >
> >
> GeoKit::Geocoders::MultiGeocoder.stub!(:geocode).and_return(fake_geocode)
> > end
> > # bulk of tests are here
> > end
> > describe ... #other tests that want real geocoding here
> > But, that just seems like a poor way to do it. I'm wondering, how can I
> > make GeoKit::Geocoders::MultiGeocoder fake by default, and then in the
> few
> > cases where I want it to be real, "un-stub" it or whatever you'd call it?
>
> I would probably write a thin wrapper around that class, exposing the
> functionality you need with the interface you want. Then in your
> tests, you can
> 1) mock that class directly
> 2) use dependency injection along with rspec-built mocks
> 3) use dependency injection with hand-built fakes
>
> The difference between 2 and 3 is that with rspec-built mocks, you're
> going to do stuff like
> @mock_geocoder = mock("geocoder", :geocode => fake_geocode)
>
> and with a hand-built fakes, you'd do something like
>
> class FakeGeocoder
> def initialize
> @geo_info_class = Struct.new(:lat, :lng, :success)
> end
>
> def geocode
> @geo_info_class.new(123.456, 789.012, true)
> end
> end
>
> You mentioned that this geocoding is a core feature of your app, so
> going with a hand-rolled fake may give you more flexibility to do some
> more sophisticated stuff. It has the added benefit of forcing you to
> really think about the abstraction in your domain since you're going
> to be implementing it twice (once as a wrapper around that class, and
> once for testing purposes).
>
> So there are the standard ways of doing DI [1]. In cases like this, a
> favorite trick of mine is to have an aliased constant that points to
> the implementation you want. For example, in development.rb you might
> have
> Geocoder = GoogleGeocoder # GoogleGeocoder is your production wrapper
>
> and in test.rb you have
> Geocoder = FakeGeocoder
>
> This is kind of a clever spin (if I do say so myself :) on the Service
> Locator [2] pattern. Basically, instead of having one central object
> that knows how to map domain abstractions to implementations, you just
> define a constant to represent the domain abstraction, and then point
> it to the real implementation you want. So now your production code
> just references Geocoder all over the place, you write unit tests for
> GoogleGeocoder to make sure it works, and you get your FakeGeocoder
> throughout your other unit tests for free.
>
> If you need to change implementations, you can just reassign the
> constant and ignore the warnings...but if you plan to have multiple
> implementations that you use throughout the app, you'll probably want
> to go for more traditional dependency injection.
>
> Pat
>
>
> [1] Jim Weirich gave a talk at OSCON 2005, the slides for which I
> can't find anymore (!!). It basically showed traditional DI and some
> neat stuff you can do with Ruby to make it much simpler.
>
> [2]
> http://www.martinfowler.com/articles/injection.html#UsingAServiceLocator
> _______________________________________________
> rspec-users mailing list
> [email protected]
> http://rubyforge.org/mailman/listinfo/rspec-users
>
--
Christopher Bailey
Cobalt Edge LLC
http://cobaltedge.com
_______________________________________________
rspec-users mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/rspec-users