---
title: Faking Remote Services with Rack::Test
teaser:
tags: web,testing,ruby
author: Joe Ferris
published_on: 2013-11-16
---

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

[rack-test]: https://github.com/brynary/rack-test
[transparent]: https://www.braintreepayments.com/docs/ruby/transactions/create_tr
[braintree]: https://www.braintreepayments.com

## How Rack::Test works

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

[stack]: https://thoughtbot.com/blog/rspec-integration-tests-with-capybara/
[webkit]: https://github.com/thoughtbot/capybara-webkit

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][fake-braintree], a library we maintain. It is a
[fake][fake] for the Braintree gateway.

[fake-braintree]: https://github.com/thoughtbot/fake_braintree
[fake]: https://thoughtbot.com/blog/fake-it

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:

* [How to Stub External Services in Tests][stub]
* [Using Capybara to Test JavaScript that Makes HTTP Requests][http]
* [Test-Driven Rails using RSpec and Capybara][test-driven]

[stub]: https://thoughtbot.com/blog/how-to-stub-external-services-in-tests/
[http]: https://thoughtbot.com/blog/using-capybara-to-test-javascript-that-makes-http/
[test-driven]: https://thoughtbot.com/upcase/test-driven-rails
