---
title: A HTTP Testing Proxy
teaser:
tags: web,testing
author: Mike Burns
published_on: 2008-07-25
---

[Hoptoad](http://hoptoadapp.com/)&mdash;**which is now live**&mdash;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.

![Battletoads](http://images.thoughtbot.com/ui/2008-7-24-battletoads-cartoon.jpg)

[The plugin](https://github.com/thoughtbot/hoptoad_notifier
"hoptoad_notifier"), I'm sure you've seen, has a private method
`#send_to_hoptoad` that handles [the dirty HTTP
stuff](http://www.webmasterworld.com/foo/3032104.htm). It looks like a more
complicated version of this:

    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

[![Dead frog controlled via a network](http://images.thoughtbot.com/ui/2008-7-24-deadfrog.jpg)](http://www.conceptlab.com/frog/)

The integration test simulates the plugin actually hitting the application.
Normally to test `#send_to_hoptoad` you'd use
[Mocha](http://mocha.rubyforge.org/) 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](http://www.murphsplace.com/gladiator/glads.html), 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:

    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:

```ruby
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 `proxy_object` module variable.

```ruby
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`):

```ruby
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](http://images.thoughtbot.com/ui/2008-7-24-posting_from_hoptoad_notifier_test.rb).

![Look ma, no OSI layer 7!](http://images.thoughtbot.com/ui/2008-7-24-integration_test.png)
