A common pattern in our apps is handling failure by notifying Airbrake. A good example is hitting a third party web service such as Twitter.
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.
This uses the Twitter gem. 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
and Twitter::Search::ERRORS
is something custom.
Stubs and spies
We use Bourne for its test spies.
Testing failure
*(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 structure. The extra newlines separate the setup, exercise, and verification phases. The test is flat; it does not use a context block.