Fork me on GitHub


Record & replay HTTP traffic for simple, reliable tests.


Betamax is a tool for mocking external HTTP resources such as web services and REST APIs in your tests. The project was inspired by the VCR library for Ruby.

You don’t want 3rd party downtime, network issues or resource constraints (such as the Twitter API’s rate limit) to break your tests. Writing custom stub web server code and configuring the application to connect to a different URI when under test is tedious and might not accurately simulate the real service.

Betamax aims to solve these problems by intercepting HTTP connections initiated by your application and returning recorded responses.

The first time a test annotated with @Betamax is run any HTTP traffic is recorded to a tape and subsequent test runs will play back the recorded HTTP response from the tape without actually connecting to the external server.

Betamax works with JUnit and Spock. Although it is written in Groovy Betamax can be used to test applications written in any JVM language so long as HTTP connections are made in a way that respects Java’s http.proxyHost and http.proxyPort system properties.

Tapes are stored to disk as YAML files and can be modified (or even created) by hand and committed to your project’s source control repository so they can be shared by other members of your team and used by your CI server. Different tests can use different tapes to simulate various response conditions. Each tape can hold multiple request/response interactions. An example tape file can be found here.


The current stable version of Betamax is 1.1.2.


Stable versions of Betamax are available from the Maven central repository. Stable and development versions are available from the Sonatype OSS Maven repository. To install with your favourite build system see below:

Please note the Maven group changed between versions 1.0 and 1.1. Make sure you are specifying the group co.freeside when referencing Betamax in your build.


To use Betamax in a project using Gradle add the following dependency to your build.gradle file:

testCompile 'co.freeside:betamax:1.1.2'


To use Betamax in a Grails app add the following to the dependencies block in your grails-app/conf/BuildConfig.groovy file:

test 'co.freeside:betamax:1.1.2'


To use Betamax in a project using Maven add the following dependency to your pom.xml file:



To use Betamax you just need to annotate your JUnit test or Spock specifications with @Betamax(tape="tape_name") and include a Recorder Rule.


import co.freeside.betamax.Betamax;
import co.freeside.betamax.Recorder;
import org.junit.*;

public class MyTest {

    @Rule public Recorder recorder = new Recorder();

    @Betamax(tape="my tape")
    public void testMethodThatAccessesExternalWebService() {



import co.freeside.betamax.Betamax
import co.freeside.betamax.Recorder
import org.junit.*
import spock.lang.*

class MySpec extends Specification {

    @Rule Recorder recorder = new Recorder()

    @Betamax(tape='my tape')
    void 'feature that accesses external web service'() {


Recording and playback

Betamax will record to the current tape when it intercepts any HTTP request that does not match anything that is already on the tape. If a matching recorded interaction is found then the proxy does not forward the request to the target URI but instead returns the previously recorded response to the client.

Matching requests

By default recorded interactions are matched based on the method and URI of the request. For most scenarios this is adequate. However, you can modify the matching behaviour by specifying a match argument on the @Betamax annotation. Any combination of instances of the co.freeside.betamax.MatchRule enum can be used. If multiple rules are used then only a recorded interaction that matches all of them will be played back. MatchRule options are:

the request method, GET, POST, etc.
the full URI of the request target. This includes any query string.
the request body. This can be useful for testing connections to RESTful services that accept POST data.
the host of the target URI. For example the host of is
the path of the target URI. For example the path of is /search.json.
the port of the target URI.
the query string of the target URI.
the fragment of the target URI. i.e. anything after a #.
the request headers. If this rule is used then all headers on the intercepted request must match those on the previously recorded request.

Tape modes

Betamax supports three different read/write modes for tapes. The tape mode is set by adding a mode argument to the @Betamax annotation.

This is the default mode. If the proxy intercepts a request that matches a recording on the tape then the recorded response is played back. Otherwise the request is forwarded to the target URI and the response recorded.
The proxy will play back responses from tape but if it intercepts an unknown request it will not forward it to the target URI or record anything, instead it responds with a 403: Forbidden status code.
The proxy will always forward the request to the target URI and record the response regardless of whether or not a matching request is already on the tape. Any existing recorded interactions will be overwritten.

Ignoring certain hosts

Sometimes you may need to have Betamax ignore traffic to certain hosts. A typical example would be if you are using Betamax when end-to-end testing a web application using something like HtmlUnit - you would not want Betamax to intercept connections to localhost as that would mean traffic between HtmlUnit and your app was recorded and played back!

In such a case you can simply configure the ignoreHosts property of the co.freeside.betamax.Recorder object. The property accepts a list of hostnames or IP addresses. These can include wildcards at the start or end, for example "*".

If you need to ignore connections to localhost you can simply set the ignoreLocalhost property to true.

Editing tape files

Tape files are stored as YAML so that they should be reasonably easy to edit by hand. HTTP request and response bodies are stored as text for most common textual MIME types. Binary data for things like images is also stored but is not practical to edit by hand. In some cases where the text contains non-printable characters then text data will be stored as binary.


Java 6

Under Java 6 it is not possible to proxy connections to URLs whose host is localhost or The workaround is to use the hostname or public IP address of the machine instead. This is a known issue that is fixed in Java 7.

Apache HttpClient

The default implementations of Apache HttpClient takes no notice of Java’s HTTP proxy settings. The Betamax proxy can only intercept traffic from HttpClient if the client instance is set up to use a ProxySelectorRoutePlanner. When Betamax is not active this will mean HttpClient traffic will be routed via the default proxy configured in Java (if any).

In a dependency injection context such as a Grails app you can just inject a proxy-configured HttpClient instance into your class-under-test.

The HttpClient library provides an implementation called SystemDefaultHttpClient that does use the JVM proxy settings. Ideally you can use that. In addition, Betamax provides a convenient HttpRoutePlanner implementation that you can use to configure instances of other HttpClient types. For example:

DefaultHttpClient client = new DefaultHttpClient();

Groovy HTTPBuilder

Groovy HTTPBuilder and its RESTClient variant are wrappers around HttpClient so the same proxy configuration needs to be applied. For example:

def http = new HTTPBuilder('')

HTTPBuilder also includes a HttpURLClient class which needs no special configuration as it uses a rather than HttpClient.

Apache HttpClient 3.x

HttpClient 3.x is no longer supported but still fairly widely used. It does not take any notice of Java’s HTTP proxy settings and does not have the HttpRoutePlanner facility that HttpClient 4.x does. This means Betamax cannot work as seamlessly. You must set the host and port of the Betamax proxy on the HttpClient instance explicitly and Betamax’s ignoreHosts and ignoreLocalhost configuration properties will be completely ignored. For example:

HttpClient client = new HttpClient();
ProxyHost proxy = new ProxyHost("localhost", recorder.getProxyPort());


The groovy-wslite library is not aware of the default JVM proxy settings so the proxy needs to be explicitly configured with a The Recorder class provides a convenience method for getting a Proxy instance. For example:

def client = new RESTClient(targetUrl) def response = client.get(path: ‘/’, proxy: recorder.proxy)


As of version 1.1 Betamax can proxy HTTPS traffic as well as HTTP. Because Betamax needs to be able to read the content of the request and response it is not actually a valid secure proxy. Betamax will only work if the certificate chain is broken.

To enable HTTP support you simply need to set the sslSupport boolean property on the Recorder instance in your test or via Betamax configuration.

HTTPS with Apache HttpClient

Apache HttpClient needs to be configured to use Betamax’s HTTPS support:



The Recorder class has some configuration properties that you can override:

the base directory where tape files are stored. Defaults to src/test/resources/betamax/tapes.
the port the Betamax proxy listens on. Defaults to 5555.
the number of milliseconds before the proxy will give up on a connection to the target server. A value of zero means the proxy will wait indefinitely. Defaults to 5000.
the default TapeMode applied to an inserted tape when the mode argument is not present on the @Betamax annotation.
a list of hosts that will be ignored by the Betamax proxy. Any requests made to these hosts will proceed normally.
if set to true the Betamax proxy will ignore connections to local addresses. This is equivalent to setting ignoreHosts to ["localhost", "", InetAddress.localHost.hostName, InetAddress.localHost.hostAddress].
if set to true the Betamax proxy will also intercept HTTPS traffic.

If you have a file called BetamaxConfig.groovy or somewhere in your classpath it will be picked up by the Recorder class.

Example BetamaxConfig.groovy script

betamax {
    tapeRoot = new File('test/fixtures/tapes')
    proxyPort = 1337
    proxyTimeout = 30000
    defaultMode = TapeMode.READ_ONLY
    ignoreHosts = ['localhost', '']
    ignoreLocalhost = true
    sslSupport = true

Example file




Betamax is a testing tool and not a spec-compliant HTTP proxy. It ignores any and all headers that would normally be used to prevent a proxy caching or storing HTTP traffic. You should ensure that sensitive information such as authentication credentials is removed from recorded tapes before committing them to your app’s source control repository.


Betamax’s GitHub repository includes an example Grails application.


Why “Betamax”?

Betamax is a JVM port of the VCR library for Ruby. It is named after Betamax, an obsolete format of Video Cassette Recorder.


Apache Software Licence, Version 2.0


Please raise issues on Betamax’s GitHub issue tracker. Forks and pull requests are more than welcome.


Betamax depends on the following libraries (you will need them available on your test classpath in order to use Betamax):

If your project gets dependencies from a Maven repository these dependencies will be automatically included for you.




Betamax is inspired by the VCR library for Ruby written by Myron Marston. Porting VCR to Groovy was suggested to me by Jim Newbery.

HTTPS support was largely the work of Lari Hotari.

The documentation is built with Jekyll, Twitter Bootstrap, LESS, Modernizr, jQuery & Google Code Prettify. The fonts are Kameron, Bitter and Source Code Pro.