Stubbing Geo-location requests using webmock

Geo-location integration is common today for every web application. We have gems on hand, to integrate Geo-location to our application. With few steps of configuration we can Geo-code our attributes ie., user addresses. But testing Geo-coding integration takes much more time than development! We currently use Google Map APIs for our work. If there are a lot of test cases, and if each test case made a Geo-location call, then we quickly cross the API threshold limit. It also takes more time to execute the test suite. It’s better to stub Geo-location calls so that we don’t need to bother with API limit and execution time.

To stub the call I am using Webmock gem which is used to stub HTTP requests. To know more about Webmock gem go through this link. For better understanding here I am going to explain this with a demo project. First clone the project from here and set up in your machine.

The main aim of the demo is Geo-code user address and displays latitude and longitude. To Geo-code the address I used ‘Geocoder’ gem. Here is the code to Geo-code an address:

   #app/models/user.rb
   geocoded_by :full_address
   after_validation :geocode

   def full_address
      [street_address, city, state, zipcode ].join(', ')
   end

It calculates latitude and longitude of the address given by the user. To calculate, it calls the Google Map API and gets lat and long. So here we stub the call and return a predefined response that we already stored in our machine. To do that we have to do some webmock configuration. Here are the steps:

1) First download the response of the Geo-location using curl. We can find this in webmock documentation.

2) Place the response file under dir ‘spec/fixtures/webmock/maps.google.com/maps/api/geocode/’.

3) Configure the webmock as follows:

  #spec/support/webmock.rb
  require 'pathname'
  require 'webmock/rspec'

  RSpec.configure do |config|
    config.fixture_path = "#{::Rails.root}/spec/fixtures"
    dir = config.fixture_path + '/webmock'
    stubs = {}
    Dir["#{dir}/**/*"].each  do |path|
      next  if File.directory? path
      uri = path.dup
      uri.slice!( "#{dir}/" )
      if File.basename(uri) == '_directory'
         uri = File.dirname(uri)+'/'
      end
      stubs["http://#{uri}"] = path
    end

    config.around(:each) do |example|
      stubs.each { |uri, path|
         WebMock::API.stub_request(:get, uri).to_return(
                                             File.new(path))
      }
      WebMock.disable_net_connect!(:allow_localhost => true)
      example.call
      WebMock.allow_net_connect!
    end
  end

It’s restricts the HTTP requests which we are sending to external hosts except localhost. Whenever we made Geo-location call, instead of calling google API, webmock returns fixture file as a response. So we are getting lat and long without calling google API which is very interesting. Awesome, right? This also makes you test code execute very fast.

Note that WebMock disables all HTTP requests. To allow localhost, you need to specify :allow_localhost => true. Similary if you want to allow some other HTTP requests, say to Facebook, you can additionally pass :allow => [ ‘www.facebook.com’] too!

Do go through the example code repository and execute the specs in demo for better understanding.

I hope you now understand the advantages of stubbing external API requests.Any feedback or suggestion would be welcome.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s