RSPEC Stub To Test Real Third Party API’s

Introduction

 

Web applications that consume third-party APIs is a common part of web development. Applications like Facebook, GitHub, and many other OAuth providers are generally used for user signups. And sometimes we use Paypal or stripe and others for more complex purposes like payments. These third-party APIs are also consumed when we deal with service-oriented architecture in which many services are interconnected.

 

Writing test cases to the Third party API is commonly ignored by many developers because of the complexity it involves and many other issues. But let me explain you a real-time issue which I have faced by ignoring test cases for third-party APIs. We were developing a large scale FBX application which needed the use of Facebook API.

 

The scenario: Generally shopping sites like Amazon will have separate Facebook pages for each country.  We were trying to run Geo-targeted ads for the UK but because of not writing the test cases for pagination the campaign matched with the wrong facebook page(US) and ads was shown for the wrong location.

 

So, even though there are some complexities involved in writing test cases for third-party APIs, it is mandatory that we write them to avoid the blunders happening.  In this article, we will see how we are testing facebook graph API using the stub

General reasons why we ignore to write the test case for 3rd party API?

Most of the time if we are consuming third-party API, we will ignore to write a test case for external API for the following reasons:

  • Connectivity issues can cause the tests to fail.
  • There might be a limit for the number of hits for that service and it might throw errors after that limit.
  • Authentication and access restrictions.
  • This may lead to slower response time and increase the payload.
  • Direct interaction with the API can have some side effects in the service
  • The Service might not have a sandbox or a staging server.
  • Using stubs and mocks for a third party API might become a tedious task, especially if the behavior is too complex or too many public API functions are used. Then mocking the client or stubbing its methods can lead to a lot of effort and maintenance work.

Importance and need of writing test cases for 3rd party API?

Example:

To explain the different ways on how to test an API we use the following example. We use a facebook client library(Koala) to request a list of fb user pages from an external service.

 


module UsersHelper

 include KoalaHelpers

 def fb_page_tokens_for_select(user)

   graph = Koala::Facebook::API.new(user.fb_graph_token)

   pages = Rails.cache.fetch("user_#{user.id}_pages", expires_in: 5.minutes) do

     Rhod.with_impatient_facebook(graph) do |fb|

       begin

         koala_read_pages(fb.get_connections('me','accounts'))

       rescue FeedUtils::FbApiException => e

         flash[:error] = "A Facebook error occured: #{e.message}"

       end

     end

   end

   return [["", user.fb_page_token]] if pages.blank?

   result = []

   pages.each do |page|

      if user.fb_page_id == page["id"]

         result = [["#{page["name"]} - (#{page["id"]})", page["access_token"]]].concat(result)

      else

         result = result.concat([["#{page["name"]} - (#{page["id"]})", page["access_token"]]])

      end

    end

    result

end

end

Below Koala helpers library handles FB graph request and response with pagination.

 


module KoalaHelpers

 def koala_read_pages(current_page)

pages = [current_page]

while current_page.next_page_params.present?

  current_page = current_page.next_page

  pages << current_page

end

pages.flatten

 end

end

WebMock

WebMock gem can also be used to stub the requests and responses, particularly the HTTP requests in ruby.

When using WebMock all other net connections except the localhost must be disabled.

 


# in spec_helper.rb
require 'webmock/rspec'
WebMock.disable_net_connect!(:allow_localhost => true)

This raises exceptions for all net connections, still allows communication via localhost and is a good way to find out what requests are sent to external services in our tests. An exception also provides details of how the specific request can be stubbed.

 


WebMock::NetConnectNotAllowedError:
[...]
You can stub this request with the following snippet:

stub_request(:get, "http://graph.facebook.com/v2.4/me/accounts").
   with(:headers => {
          'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
          'Content-Type'=>'application/json',
          'User-Agent'=>'Faraday v0.9.1'
        }).
   to_return(:status => 200, :body => "", :headers => {})

This stubs the HTTP request (type GET) for the URL http://graph.facebook.com/v2.4/me/accounts. Let’s stub the request in the test of our example.

 


require 'spec_helper'

require 'koala_helpers'

include KoalaHelpers

describe KoalaHelpers do

 before(:each) do

first_page = {

  "data" => ['data_1', 'data_2', 'data_3'],

  "paging" => paging

}

@api = Koala::Facebook::API.new("123")

@collection = Koala::Facebook::GraphCollection.new(first_page, @api)

 end

 describe "#next_page with next parameters" do

let(:paging){ {"next" => "http://graph.facebook.com/v2.4/me/accounts?a=2&b=3"} }

before(:each) do

  second_page = {

    "data" => ['data_4', 'data_5']

  }

  # Stub for koala#next_page call

  stub_request(:get, "https://graph.facebook.com/v2.4/me/accounts?a=2&access_token=123&b=3").with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Faraday v0.8.9'}).

    to_return(:status => 200, :body => {data: Koala::Facebook::GraphCollection.new(second_page, @api) }.to_json)

end

it "should return the next page of results" do

  result = koala_read_pages(@collection)

  expect(result).not_to be_empty

  expect(result.count).to  eq(5)

end

 end

 describe "#without next page parameters" do

let(:paging){ {:next => '' } }

it "should return the without next page of results" do

  result = koala_read_pages(@collection)

  expect(result).not_to be_empty

  expect(result.count).to  eq(3)

end

 end

end

VCR

Recording the live interaction and replaying the interactions during the tests can also be done to prevent any external requests. Using VCR gem we can record the test suites outgoing HTTP requests which can be replayed in future test runs. But for this, the external service should be available for the first test run.

Conclusion

So as discussed earlier despite many difficulties or issues that occur it is mandatory to write test cases for third-party APIs. You can decide the way you test the API depending on the context of the service. To read more on web application development follow AgiraTechnologies