---
title: Writing An API Client With Test, Staging, And Production In Mind
teaser: How to work with different environments during API development.
tags: web,ruby
author: Dan Croak
published_on: 2011-09-15
---

I was recently tasked with this story:

    Background:
      Given I go to the sign up page
      And I fill in "Email" with "new@example.com"
      And I fill in "Password" with "password"

    Scenario: Visitor signs up and does not want email
      When I press "Sign up"
      Then email service is not notified "new@example.com" signed up

    Scenario: Visitor signs up and wants email
      When I check "Please email me"
      And I press "Sign up"
      Then email service is notified "new@example.com" signed up

The email service in question was internal, accessible via an HTTP API, and did
not have a Ruby client library.

So, I had to write and test my own. The usual approach would involve using a
HTTP stubbing library like Sham Rack or
[Artifice](https://github.com/wycats/artifice). I decided to try something
different and see how it felt.

First, I needed an interface that I could test:

    Then /^email service is notified "([^"]*)" signed up$/ do |email|
      EmailService.notifications.should include(email)
    end

    Then /^email service is not notified "([^"]*)" signed up$/ do |email|
      EmailService.notifications.should_not include(email)
    end

I'm re-using a pattern ("store data in a simple array for easy state-based
testing") we've used before for [Javascript integration testing
Mixpanel](https://thoughtbot.com/blog/post/4026880618/javascript-integration-t
esting-example-installing-and).

So I have the start to an `EmailService` interface. I thought I wanted it
invoked as part of an `after_save` callback on the `User` model, so here's that
spec:

    describe User, 'who opts into email' do
      subject { build(:user, email_opt_in: true) }

      before do
        EmailService.stubs(:notify)
        subject.save
      end

      it 'notifies EmailService' do
        EmailService.should have_received(:notify).with(subject.email)
      end
    end

    describe User, 'who does not opt into email' do
      subject { build(:user, email_opt_in: false) }

      before do
        EmailService.stubs(:notify)
        subject.save
      end

      it 'does not notify EmailService' do
        EmailService.should have_received(:notify).never
      end
    end

This is the [stubbing and
spying](https://thoughtbot.com/blog/post/159805295/spy-vs-spy) technique and
uses RSpec, mocha, and [bourne](http://github.com/thoughtbot/bourne).

My thought process was that I needed an active verb, `notify` to invoke when the
user is created, but that will store the invocation in a `notifications` array
that the Cucumber step definition needs to check state (I don't want to stub,
spy, or mock in an integration test).

So, making the user spec pass isn't bad:

    require 'email_service/notifier'

    class User < ActiveRecord::Base
      after_create do
        if email_opt_in?
          EmailService.notify(email)
        end
      end
    end

Now, the `EmailService` can be spec'ed. We already had [John
Nunemaker's](http://addictedtonew.com/about/)
[HTTParty](http://httparty.rubyforge.org/) as a dependency in the app, and I
only had to make one HTTP POST, so I knew I would be re-using HTTParty's
interface.

    describe EmailService::Notifier, '#post' do
      subject { EmailService::Notifier }

      let(:email) { 'new-signup@example.com' }

      before do
        subject.stubs(:post)
        subject.new(email).post
      end

      it 'POSTs to email service with API_KEY and given email' do
        subject.should have_received(:post).with(
          subject::URL, query: { api_key: subject::PARAMS, email: email }
        )
      end
    end

What I care about here is that during the one POST the app has to make, that the
parameters are correct. This is close to hitting the live service as I'm willing
to get without making an HTTP request.

I had an internal debate with myself while writing it over whether this is
"stubbing the [system under test](http://xunitpatterns.com/SUT.html)"
(considered bad practice). I decided "no" because despite the `subject` being
stubbed, the system under test is actually the `EmailService::Notifier#post`
method.

The stubbed and spied method is also mixed in from HTTParty, so I feel clean
with this approach.

Making it pass:

```ruby
require 'httparty'

module EmailService
  class << self
    attr_accessor :notifications
  end

  def self.notify(email, live = false)
    if Rails.env.production? || live
      Notifier.new(email).post
    else
      self.notifications << email
    end
  end

  class Notifier
    include HTTParty

    API_KEY = 12345
    URL = 'http://emailservice.example.com'

    def initialize(email)
      @url   = URL
      @email = email
    end

    def post
      self.class.post(@url, query: { api_key: API_KEY, email: @email })
    end
  end
end
```

## Summary

* I've got an environment-specific conditional inside the true public interface
  for the service, `EmailService.notify`, which is used by the `User` model.
* In my integration test and in the development environment, all that happens
  is an array is populated, which makes it easy to confirm that it was notified
  correctly.
* I've got a unit test that makes sure the correct URL and
  <abbr title="Application Programming Interface">API</abbr> key are used.
* I've got a way to override `EmailService.notify` with the `live` flag so I
  can invoke it from the Rails console on production or staging when testing or
  debugging.

This almost takes longer to describe than to code but I'm curious what people
think about this style of writing an <abbr title="Application Programming
Interface">API</abbr> client with test, staging, and production environments in
mind. How do you do things differently?
