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:
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 .
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:
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.
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):
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.
