Suppose I have this very simple method in Store's model:
def geocode_address
loc = Store.geocode(address)
self.lat = loc.lat
self.lng = loc.lng
end
If I want to write some test scripts that aren't affected by the geocoding service, which may be down, have limitations or depend on my internet connection, how do I mock out the geocoding service? If I could pass a geocoding object into the method, it would be easy, but I don't see how I cou开发者_C百科ld do it in this case.
Thanks!
Tristanusing rspecs built in mocking and stubbing, you could do something like this:
setup do
@subject = MyClass.new
end
it 'handles geocoder success' do
mock_geo = mock('result', :lat => 1, :lng => 1)
Store.stub!(:geocode).and_return mock_geo
@subject.geocode_address
@subject.lat.should == mock_geo.lat
@subject.lng.should == mock_geo.lng
end
it 'handles geocoder errors' do
Store.stub!(:geocode).and_raise Exception
@subject.geocode_address
@subject.lat.should == _something_reasonable_
@subject.lng.should == _something_reasonable_
end
Using Double-R (RR) https://github.com/btakita/rr, it's simple:
test 'should mock the geocoding service' do
store = Store.new
mock_location = mock(Object.new)
mock_location.lat{1.234}
mock_location.lng{5.678}
mock(Store).geocode.with_any_args{mock_location}
store.geocode_address
assert_equal 1.234, store.lat
assert_equal 5.678, store.lng
end
If there's no way to mock a service then it shows a poor design. The service should be separate from the model (whatever a Store
is). You just need to refactor into a more de-coupled system, which will both allow you to mock it, and make the system easier to maintain.
精彩评论