开发者

How do I mock an object in this case? no obvious way to replace object with mock

开发者 https://www.devze.com 2023-02-08 13:06 出处:网络
Suppose I have this very simple method in Store\'s model: def geocode_address loc = Store.geocode(address)

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!

Tristan


using 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.

0

精彩评论

暂无评论...
验证码 换一张
取 消