Cucumber, Service Based Applications, Middleware, and You


Over at Primedia, we’ve been trying to get a little bit better at our BDD, and have resolved to really use Cucumber wherever possible. This is an easy thing to say for most web apps, however ours have a number of critical parts that are external to the application itself. This means that full integration testing of the applications that use these services as well as testing the services themselves can be rather tricky. In order for us to get started there were two major problems that we had to tackle.

The first was how to test the service based apps outside of any consuming application. The specific one in question did not present any HTML pages at all, and only returned Javascript that was executed. A few ideas were floated around like dev/test only controllers or basic one-off apps that were specifically for testing the service. Eventually we cam across a solution that worked and seemed much cleaner. We combined the one-off app idea with rack middleware to create an application within our app that was only loaded in certain environments. We used Sinatra to make a simple application that would respond with test pages on routes prefixed with ‘/test’, and then added it with config.middleware.use. Combining this with Celerity and Capybara gave us a very clean testing solution for an application that only served Javascript.

There is one gotcha to this, and it only involves using Sinatra as middleware in Rails. Rails attempts to determine if the middleware you gave it is a proc, and in doing so prevents you from being able to use any middleware that can respond to Rack requests as a singleton (like Sinatra). Our quick fix was to monkey patch Sinatra to remove self.call, but a more permanent solution is fixing the way Rails 2.3 does its middleware. I hope to submit some patches for that soon.

module Sinatra
  class Base
    class << self
      remove_method :call
    end
  end
end

Our second hurdle was determining how to test our consuming apps using Cucumber. The main problem we had is that we use reverse proxying to make the services appear to be coming from the same domains as the rest of the apps. This was easily taken care of in manual tests, where we could simulate our load balancers using Apache or nginx, however with automated tests, this becomes a problem. The automated test suite doesn't run behind a proxying layer or a web server, it just hits the app directly. So, tackling that problem, I am releasing a gem I developed called rack-reverse-proxy. What this gem does is allow you to put reverse proxy rules directly into your middleware stack so they are independent of your web server. This is probably not a good idea for production systems, load balancers and web servers are much better at this sort of thing, and due to the way rack works I have to wait until the entire request is received before forwarding it. However it's great for development and testing, allowing you to test your app directly without the need of an intermediate web server layer.

  require 'rack/reverse_proxy'

  use Rack::ReverseProxy do
    # Forward the path /test* to http://example.com/test*
    reverse_proxy '/test', 'http://example.com/'

    # Forward the path /foo/* to http://example.com/bar/*
    reverse_proxy /^\/foo(\/.*)$/, 'http://example.com/bar$1'
  end

, , , , , ,