How to test external API  

29 Mar 2012
William Notowidagdo Kiranatama Staff
Knowledge


Using external API as a data source is a common use case in any modern web app. This could be a problem when we start to testing. Any test on the part that use the data source must make request to the actual external API.

App specification

Should be a Rails 3.2 app which has a homepage that display list of recent headlines from ESPN. The homepage need to get the content from the ESPN API.

Here is a typical implementation of above app specification:

  1. A request for the homepage is route to Headlines controller
  2. Headlines controller asks Headline model for all recent headlines
  3. The Headline model gets the latest headlines from the ESPN API

Testing strategy

To test this I'll:

  1. Start with integration test. I will use RSpec Rails
  2. Use vcr to record the actual ESPN API HTTP request and response

Start with integration test

Because I don't know the latest headlines on ESPN at the time this spec is run, I only specify an expected DOM structure

#spec/requests/homepage_spec.rb

require 'spec_helper'

describe 'The homepage' do
  it 'displays recent headlines from ESPN' do
    visit '/'
    headlines_links = all '#headlines .item a'
    headlines_links.should_not be_empty
    headlines_links.each do |link|
      link[:href].should match("espn.go.com")
    end
  end
end

Configure vcr in your spec/spec_helper.rb

require 'vcr'

VCR.configure do |config|
  config.cassette_library_dir = 'spec/cassettes'
  config.hook_into :fakeweb
  config.configure_rspec_metadata!
end

With the configuration above, when you run the spec, vcr will record the HTTP request to a .yml file in spec/casettes. Run it again, vcr will replay the response from designated URL where the HTTP request is made. No real HTTP request are made anymore.

Staying up-to-date

At this point, if the ESPN headlines changes, the specs still continue to pass. We need to keep our recorded "cassette" up-to-date.

Add below line into VCR.configure block in spec/spec_helper.rb.

config.default_cassette_options = {
  re_record_interval: 1.week
}

The above tells vcr to re-record all HTTP request and response that are older than 1 week.

Summary

By using vcr to record HTTP request and replay the response, your spec if much faster since no real HTTP request are made after the first run. Also it made your test more deterministic and accurate.

In this post I focus to show you how to utilize VCR on your spec. There are some missed best-practices here for example you should put the HTTP request implementation in a library instead of model.

You can check out the complete app https://github.com/kiranatama/blog-apps/tree/master/headlines