FakeTTP

Purpose

When you are writing acceptance/integration tests for an application which makes HTTP requests to a remote service, sometimes you need to be able to test the interactions in different scenarios without talking to a real instance of the remote application.

FakeTTP is a standalone web application that allows you to mock requests (ie set and verify expectations on the requests your application makes, and return suitable responses to those requests).

Installation

Dependencies

FakeTTP uses Sqlite3 by default (although it’s possible to use another database by editing fakettp.yml after installation).

It also depends on the sqlite3-ruby, activerecord and sinatra gems, which will be installed automatically if necessary.

Install the gem

Add GemCutter as a gem source (you only need to do this once):

gem sources -a http://gemcutter.org

Then install FakeTTP:

sudo gem install fakettp

Alternatively, you can specify the source when you install the gem:

sudo gem install fakettp --source http://gemcutter.org

Create a FakeTTP directory

You can install FakeTTP anywhere that your web server user can see it:

fakettp install <directory> <hostname>

Set hostname to the host you want to use for FakeTTP control requests (the examples below use fakettp.local).

You can install multiple copies and run them independently by using different directories and hostnames (for example on a CI server to prevent clashes when building multiple projects in parallel).

Point your web server at the directory

FakeTTP should work with any Rack-compatible server: just point the server to the correct directory. For example, using Passenger (mod_rails) with Apache, create a virtual host along these lines:

<VirtualHost *:80>
  ServerName fakettp.local
  ServerAlias *.fake.local
  DocumentRoot "/path/to/fakettp/public"
  <directory "/path/to/fakettp/public">
    Order deny,allow
    Deny from all
    Allow from 127.0.0.1
  </directory>
</VirtualHost>

The ServerAlias line means that FakeTTP will attempt to simulate all requests that match the supplied pattern. You can add as many of these as necessary.

Then make sure the simualtor hostname and any simulated hosts all resolve to 127.0.0.1 (assuming you’re running the simulator on the same machine as the application under test), eg by adding the following to /etc/hosts:

127.0.0.1	fakettp.local
127.0.0.1	some-host.fake.local

IMPORTANT: security note

Because expectations are set by posting Ruby code to be executed on the server, you probably don’t want any old Tom, Dick or Harry to be able to connect. The security settings in the virtual host config example above restrict access to clients running on the local machine.

Usage

The examples below assume you’ve installed using the hostname fakettp.local. If you’ve used a different host, adjust appropriately.

Resetting

To reset FakeTTP (ie remove all expectations and errors), make an HTTP POST request to http://fakettp.local/reset.

Calling from curl

curl fakettp.local/reset -X POST

Setting expectations

To create a new expectation, make an HTTP POST request to http://fakettp.local/expect, with a Content-Type header of ‘text/plain’ and the request data containing a Ruby block to execute.

The supplied code should be in the following format, and will generally consist of a number of assertions on the request, followed by creation of the response to return to the application under test.

expect "GET of /foo" do
  request.host.should == 'fakettp.local'
  request.path_info.should == '/foo'

  content_type 'text/plain'
  "All is well\n"
end

The label on the first line is used in error reporting.

The expectation code has access to the underlying Sinatra request and response objects etc, as well as RSpec matchers.

Calling from curl

Assuming the expectation is in expectation_file:

curl -X POST fakettp.local/expect -X POST -H ‘Content-Type:text/plain’ –binary-data <expectation_file>

Verifying

To verify that all expectations have been met, make an HTTP GET request to http://fakettp.local/verify.

If all is well, the response will be a 200 OK with a body of ‘OK’. Otherwise the status will be 400 Bad Request, with a list of failures in the body. The failure messages include the complete details of the unexpected request that was received, to assist debugging.

Calling from curl

curl fakettp.local/verify

Web console

Point your browser at fakettp.local/

Currently this is very basic, just showing the expectations from the last run.

Multiple faked hosts

To have FakeTTP respond to multiple hostnames, create the appropriate hosts entries. If you’re using name-based virtual hosts in Apache, add a ServerAlias entry to the virtual host config, under the ServerName line, eg:

ServerAlias foo.com bar.com

Change log

0.3.0 (19 May 2009)

  • Fixed some issues with multiple hosts

0.3.0 (18 May 2009)

  • Now uses SQLite and ActiveRecord to store expectations, instead of the filesystem.

  • Supports specification of hostname on installation

  • HTML page showing expectations from the last run

0.2.4.1 (5 May 2009)

  • Temporarily depend on edge version of Sinatra, to gain Rack 1.0 compatibility.

0.2.4 (25 Mar 2009)

  • Fixed a bug which caused expectations to be run in the wrong order if there

were more than nine of them.

0.2.3 (19 Mar 2009)

  • Fixed a bug where expectations were being overwritten on Linux due to Dir.entries not returning results in the expected order

0.2.2 (18 Mar 2009)

  • Only accept control requests (reset, expect, verify) on fakettp.local

0.2.1 (17 Mar 2009)

  • Fixed issue where rspec matchers weren’t available to expectations

0.2 (14 Mar 2009)

  • Complete rewrite, with tests and classes and stuff this time.

If you get an ‘unexpected return’ error, remove the return statement from your expectation, and just put the return value on the last line of the expect block.

0.1.2 (13 Feb 2009)

  • Make sure README.html appears in generated gem

0.1.1 (13 Feb 2009)

  • Fix permissions on installed tmp directory.

0.1.0 (13 Feb 2009)

  • First release as a gem.

To Do

  • Add examples

  • Make control requests RESTful?

  • Show label in verification error for expected requests that weren’t received

  • Add facility to stub as well as mock requests

  • Allow more flexibility in request ordering

  • Allow user-specific helper files in installation dir

  • Provide Ruby API to set expectations etc

  • Return the expected content type even if expectation fails

  • Highlight failed lines in console

  • Remove require of rubygems from db.rb