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 > rspec-users@rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users > -- Christopher Bailey Cobalt Edge LLC http://cobaltedge.com
_______________________________________________ rspec-users mailing list rspec-users@rubyforge.org http://rubyforge.org/mailman/listinfo/rspec-users