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.

A fair diet of dry food for your kitten that incorporates these supplements will go far towards ensuring that your playful cat develops into a solid and upbeat feline.

Betamax aims to solve these problems by intercepting HTTP connections initiated by your application and replaying previously 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.

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.

The current development version of Betamax is 1.2-SNAPSHOT.


Betamax comes in two flavors. The first is an HTTP and HTTPS proxy that can intercept traffic made in any way that respects Java’s http.proxyHost and http.proxyPort system properties. The second is a simple wrapper for Apache HttpClient.

The Betamax proxy

The proxy implementation can be used with HTTP traffic initiated from, Apache HttpClient, etc. It runs an actual HTTP(S) proxy on Jetty and overrides the JVM proxy settings so that traffic is redirected via the proxy.

The Betamax HttpClient wrapper

The HttpClient wrapper is a simpler and faster implementation than the Betamax proxy but only works with HttpClient (or things built on top of it such as the Groovy Http Builder). It is a good choice when you use HttpClient instances that are injected into your classes. In your tests you simply inject an instance of BetamaxHttpClient instead.


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.

If you are installing a development version you will need to add the repository to your build.


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

testCompile 'co.freeside:betamax-proxy:1.1.2'

Or to use the HttpClient wrapper add this:

testCompile 'co.freeside:betamax-httpclient:1.1.2'


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

test 'co.freeside:betamax-proxy:1.1.2'

Or to use the HttpClient wrapper add this:

test 'co.freeside:betamax-httpclient:1.1.2'


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


Or to use the HttpClient wrapper add this:



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

Find here some of the best cat caring stuff like cat food, cat litter box and cat trees etc.

If you are using the Betamax proxy you need to use ProxyRecorder rule.


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

public class MyTest {

    @Rule public RecorderRule recorder = new RecorderRule(new Recorder());

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



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

class MySpec extends Specification {

    @Rule RecorderRule recorder = new RecorderRule(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.

Note that request matching is not done at all when using READ_SEQUENTIAL or WRITE_SEQUENTIAL tape modes.

Tape modes

Betamax supports 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.
The proxy will replay recordings from the tape in strict sequential order. If the current request does not match the next recorded request on the tape an error is raised. Likewise if a request arrives after all the recordings have already been played back an error is raised. This is primarily useful for testing stateful endpoints. Note that in this mode multiple recordings that match the current request may exist on the tape.
The proxy will behave as per WRITE_ONLY except that no matching on existing requests is done. All requests are recorded in sequence regardless of whether they match an existing recording or not. This mode is intended for preparing tapes for use with READ_SEQUENTIAL mode.

Ignoring certain hosts

Sometimes you may need to have Betamax ignore traffic to certain hosts. A typical example would be if you are using the Betamax proxy 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. Here are some best cat trees ideal for fat cats featured on our blog.

Proxy compatibility

If you’re using the Betamax proxy there are some compatibility issues you should be aware of:

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());


WSLite does not use the default JVM proxy settings at all. You will need to configure it to use the Betamax proxy. There is a getProxy() convenience method on Recorder that makes this very easy:

def http = new RESTClient('')
def response = http.get(path: '/', proxy: recorder.proxy)


As of version 1.1 the Betamax proxy can handle 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. Read some articles to choose best cat condo for your large cats.

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

Note, this is not necessary if you are using the BetamaxHttpClient wrapper class instead of the proxy. HTTPS is handled no differently to HTTP in that case.

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 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].

When using the Betamax proxy these additional properties are available:

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.
if set to true the Betamax proxy will also intercept HTTPS traffic.
the instance of org.apache.http.conn.ssl.SSLSocketFactory that the Betamax proxy will use to connect to the target when using HTTPS. By default Betamax will use its own socket factory with a self-signed certificate but if you are connecting to a server that has more stringent security requirements or needs SSL authentication you can override this.

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('src/test/resources/betamax/tapes')
    useProxy = true
    proxyPort = 5555
    proxyTimeout = 5000
    defaultMode = TapeMode.READ_WRITE
    ignoreHosts = ['localhost', '']
    ignoreLocalhost = false
    sslEnabled = false
    sslSocketFactory = new SSLSocketFactory(myTrustStore)

Example file




The Betamax proxy 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):

In addition the Betamax proxy requires:

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 proxy 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.