---
title: Testing HTTP Errors With Ruby
teaser:
tags: web,ruby,testing,airbrake
author: Dan Croak
published_on: 2009-09-06
---

A common pattern in our apps is handling failure by notifying
[Airbrake](http://airbrake.io). A good example is hitting a third party web
service such as Twitter.

```ruby
def fetch_tweets(search)
  search.fetch
rescue *(HTTP_ERRORS + Twitter::Search::ERRORS) => error
  HoptoadNotifier.notify(error)
  []
end
```

We send rescued errors to Airbrake, shielding our users from 500s with relevant
copy.

![Thunder Thimble Errno errors in Hoptoad](https://thoughtbot-training.s3.amazonaws.com/images/thunder_thimble_errno.png)

This uses the [Twitter gem](https://github.com/sferik/twitter). It separates
building a query (object initialization) and making the HTTP request (fetch
method).

## Isolating HTTP calls

Somewhere else, we do heavy-duty query building. We then isolate the HTTP
request inside it's own method and rescue an expected class of errors.

Aside from testing failure, this isolation makes our other tests easier. We can
stub the `fetch_tweets` method and forget about the interface to the Twitter
library.

## Arrays of error types

`HTTP_ERRORS` comes from the [Suspenders' errors
initializer](https://github.com/thoughtbot/suspenders/blob/master/templates/errors.rb)
and `Twitter::Search::ERRORS` is something custom.

## Stubs and spies

We use [Bourne](http://github.com/thoughtbot/borne) for its [test spies](https://thoughtbot.com/blog/post/159805295/spy-vs-spy).

## Testing failure

```ruby
*(HTTP_ERRORS + Twitter::Search::ERRORS).each do |error|
  should "notify hoptoad upon #{error} on fetch tweets" do
    brand = build(:brand)
    search = stub('search')
    search.stubs(:fetch).raises(error.new(''))
    Airbrake.stubs(:notify)

    brand.fetch_tweets(search)

    assert_received(Airbrake, :notify) do |expects|
      expects.with(is_a(error))
    end
  end
end
```

This is a classic stub-and-spy test. It uses the normal [Four-Phase
Test](https://thoughtbot.com/blog/post/32455387133/four-phase-test) structure.
The extra newlines separate the setup, exercise, and verification phases. The
test is flat; it does not use a context block.
