A HTTP testing proxy
Posted by Mike Burns
Jul 25
Hoptoad—which is now live—is both an application and a Rails plugin that must work together. This integration simply cannot go untested in a test-happy place like thoughtbot.

The plugin, I'm sure you've seen, has a private method #send_to_hoptoad that handles the dirty HTTP stuff. It looks like a more complicated version of this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
def send_to_hoptoad(data) url = HoptoadNotifier.url Net::HTTP.start(url.host, url.port) do |http| headers = { 'Content-type' => 'application/x-yaml', 'Accept' => 'text/xml, application/xml' } response = begin http.post(url.path, stringify_keys(data).to_yaml, headers) rescue TimeoutError => e nil end case response when Net::HTTPSuccess then logger.info "Hoptoad Success" else logger.error "Hoptoad Failure" end end end |
The integration test simulates the plugin actually hitting the application. Normally to test #send_to_hoptoad you'd use Mocha to stub out Net::HTTP methods, but stubbing sweeps away too many potential issues here.
What we really want is an integration test that pits the plugin against the real application, without running a server. We want Net::HTTP#post to use ActionController::Integration::Session#post .
The gruesome internals
In the integration test for the application, first require in the needed tricks:
1 2 3 |
require 'test_helper' require 'net/http' require File.dirname(__FILE__) + '/../lib/hoptoad_notifier/lib/hoptoad_notifier' |
(we've installed a copy of the plugin into test/lib)
Then, open up Net::HTTP and get rid of the bits that connect to the network. This part could be done with Mocha, but we need to open Net::HTTP later so we might as well do it this way:
1 2 3 4 |
class Net::HTTP < Net::Protocol def connect end end |
While you have Net::HTTP open, replace #post with a proxy. The class to proxy to is passed into the test_unit_class module variable.
1 2 3 4 5 6 7 |
class Net::HTTP < Net::Protocol mattr_accessor :proxy_object def post(path, body, headers) self.class.proxy_object.post path, body, headers end end |
Finally in the test setup block we need to initialize Net::HTTP with the appropriate instance of ActionController::Integration::Session (which is to say, self):
1 2 3 4 5 6 7 8 9 10 |
class PostingFromHoptoadNotifierTest < ActionController::IntegrationTest context "with a connection from the plugin to the application" do setup do Net::HTTP::proxy_object = self end should_eventually "deny access to people who disagree with me" do end end end |
All #should statements inside the context will proxy themselves through the integration test instead of hitting the network. Bam!
Check out the complete test file.


Comments on this post
Jul 25
atmos said,
I can’t believe that dude’s hostname is stripper. You guys set the bar so high!
With mocking/stubbing getting so much press it’s nice to see people guiding folks in the right direction. Well done as always.
Jul 26
Aaron Gibralter said,
Is the part that sends data to Hoptoad blocking? I guess with a 2 or 5 second timeout it’s not so bad… But what about offering the ability to offload the sending operation to workling or backgroundrb, etc.?
Jul 26
Matt Jankowski said,
Aaron – yes, that’s one of the last remaining issues from the EN that we plan on correcting. As it stands now there is a shortish timeout, so it won’t block forever…but any waiting at all is not really acceptable.
Our plan is to offload the reporting in a queue based system in the plugin itself, so that control can return to the framework immediately after the error is caught, and errors can be reported up to the web service in the background.
This is not in place yet, but it’s high on the list for next iteration.
Jul 27
Ken Collins said,
The hoptoad account signup seems to not be working. Keeps not allowing newly created accounts to login.
Great article too! Coincidently, I just mentioned the Battle Toads game to my wife yesterday after we hooked up the 8-bit Nintendo to the 47” TV :)
Jul 29
Aaron Gibralter said,
I’m just curious why you ignore ActionController::RoutingError. I thought one of the reasons you created Hoptoad was that EN sent you too many emails for simple errors. Now that you have Hoptoad, shouldn’t you not ignore the simple, non-critical errors?
Jul 30
Jon Yurek said,
@Aaron: Well, yes and no. You’re right in that they’re simple errors, but we don’t generally care about them since they’re basically just 404s (and are almost all generated by spiders and spambots), plus they have the odd problem of defeating the duplicate detection we have (which is keyed to, among other things, action and controller), so we still get a lot of emails because they all appear to be different errors.
Sorry, comments are closed for this article.
© 2000 - 2009 by thoughtbot, inc.
written by a bushel of tiny robots
Widgetfinger: Simple content management for simple websites.
Come “ride the toad” on Hoptoad, the app error app.
Tee-Bot, funny shirts your friends won't understand!
Umbrella Today: “It’s like totally the simplest weather report ever, Julie.”
Thoughtbot
thoughtbot is a technology consulting firm that provides web application development and design services. We focus on building modern systems, embracing good ideas and delivering elegant solutions.
Interested in learning Rails?
Sign up for our training.
Archives