Faking Remote Services with Rack::Test

Joe Ferris

Using Rack middleware, we can reroute Rack::Test requests to fake remote services without spinning up servers. This example shows how to use that technique to handle the Transparent Redirect HTTP requests for the Braintree payment gateway.

How Rack::Test works

Our typical integration test stack is RSpec and Capybara. We use different drivers for different situations, such as Capybara Webkit when we need to run JavaScript in a headless browser.

The default driver for Capybara is Rack::Test. It is one of the fastest drivers because it interacts directly with Rack interfaces and doesn’t require an external service to be started.

Out of the box, Rack::Test will hit the same Rack application for every request. So, we’ll use Rack middleware to re-route Rack::Test requests based on their port number.

PortMap Rack middleware

Middleware to re-route requests based on their port number:

class PortMap
  def initialize(default_app, mappings)
    @default_app = default_app
    @mappings = mappings
  end

  def call(env)
    request = Rack::Request.new(env)
    port = request.port
    app = @mappings[port] || @default_app
    app.call(env)
  end
end

We can now use this in a Capybara + Rack::Test suite:

original_app = Capybara.app

Capybara.app = PortMap.new(
  original_app,
  1234 => MyFakeCreditCardGateway,
  5678 => MyFakeOauthServer
)

FakeBraintree

To use PortMap more specifically for the Transparent Redirect use case, we would include FakeBraintree, a library we maintain. It is a fake for the Braintree gateway.

This will now route Braintree requests to FakeBraintree:

Capybara.app = PortMap.new(
  original_app,
  ENV['GATEWAY_PORT'] => FakeBraintree::SinatraApp
)

Similar middleware

We could also write middleware to reroute requests based on the host name, path, or anything else in the request, faking remote services without spinning up servers.

What’s next

If you found this useful, you might also enjoy: