Software testing tool and service: Betamix

INTRODUCTION Betamix is a tool for simulating 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 need third-party downtime,...

Software testing tool and service: Betamix

INTRODUCTION

Betamix is a tool for simulating 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 need third-party downtime, network issues, or resource constraints (like Twitter API rate limiting to break your tests). Writing your own stub web server code and configuring your application to connect to a different URI during testing is time consuming and may not accurately simulate the actual service.

Betamix aims to address these issues by intercepting HTTP connections initiated by your application and replaying previously recorded responses. If it sounds unusual to you, our experts are ready to provide software testing services to come up with great solutions.

The first time you run a test tagged with @Betamix, any HTTP traffic is recorded to tape and subsequent test runs will play the recorded HTTP response from the tape without actually connecting to an external server.

Betamix works with JUnit and Spock. Although written in Groovy, Betamix can be used to test applications written in any JVM language.

Tapes are stored on disk as YAML files and can be manually created or modified, or stored in your project’s source control repository so that they can be shared with other team members and used by your CI server. Different tests may use different tapes to simulate different trigger conditions. Each feed can contain multiple request / response interactions. An example tape file can be found here.

VERSIONS

The current stable version of Betamix is 1.1.2.

The current development version of Betamix is 1.2-SNAPSHOT.

IMPLEMENTATIONS

Betamix comes in two flavors. The first is a HTTP and HTTPS intermediary that can capture traffic made in any capacity that regards Java’s http.proxyHost and http.proxyPort framework properties. The second is a straightforward covering for Apache HttpClient.

The Betamix proxy

The proxy implementation can be used with HTTP traffic initiated from java.net.URLConnection, 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 Betamix HttpClient wrapper

The HttpClient wrapper is a simpler and faster implementation than the Betamix 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 BetamixHttpClient instead.

INSTALLATION

Stable forms of Betamix are accessible from the Maven focal vault. Stable and improvement adaptations are accessible from the Sonatype OSS Maven store. To introduce with your beloved form framework see beneath.

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

If you are installing a development version you will need to add the repository http://oss.sonatype.org/content/groups/public/ to your build.

  • Gradle
  • Grails
  • Maven

Gradle

To utilize the Betamix intermediary in a task utilizing Gradle add the accompanying reliance to your build.gradle record:

testCompile ‘co.freeside:betamix-proxy:1.1.2’

Or to use the HttpClient wrapper add this:

testCompile ‘co.freeside:betamix-httpclient:1.1.2’

USAGE

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

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

  • JUnit
  • Spock

JUnit

import co.freeside.betamix.junit.Betamix;

import co.freeside.betamix.junit.RecorderRule;

import co.freeside.betamix.Recorder;

import org.junit.*;

public class MyTest {

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

    @Betamix(tape=”my tape”)

    @Test

    public void testMethodThatAccessesExternalWebService() {

    }

}

Recording and playback

Betamix will keep in touch with the current tape when it catches any HTTP demand that doesn’t coordinate with anything currently on the tape. On the off chance that a coordinating with recorded cooperation is found, the intermediary doesn’t advance the solicitation to the objective URI, however rather returns the recently recorded reaction to the customer.

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 @Betamix annotation. Any combination of instances of the co.freeside.betamix.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:

method

the request method, GETPOST, etc.

uri

the full URI of the request target. This includes any query string.

body

the request body. This can be useful for testing connections to RESTful services that accept POST data.

host

the host of the target URI. For example the host of http://search.facebook.com/search.json is search.facebook.com.

path

the path of the target URI. For example the path of http://search.facebook.com/search.json is /search.json.

port

the port of the target URI.

query

the query string of the target URI.

fragment

the fragment of the target URI. i.e. anything after a #.

headers

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

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

READ_WRITE

This is the default mode. On the off chance that the intermediary catches a solicitation that coordinates with a recording on the tape then the recorded reaction is played back.  Otherwise the request is forwarded to the target URI and the response recorded.

READ_ONLY

The intermediary will play back reactions from tape yet if it blocks an obscure solicitation it won’t advance it to the objective URI or record anything, rather it reacts with a 403: Forbidden status code.

WRITE_ONLY

The intermediary will consistently advance the solicitation to the objective URI and record the reaction whether or not or not a coordinating with demand is as of now on the tape. Any current recorded connections will be overwritten.

READ_SEQUENTIAL

The intermediary will replay accounts from the tape in severe successive request. If the current solicitation doesn’t coordinate with the following recorded solicitation on the tape a mistake is raised. Moreover if a solicitation shows up after every one of the accounts have effectively been played back a mistake 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.

WRITE_SEQUENTIAL

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

A regular model would be in case you are utilizing the Betamix’123x’1234r6 proxy intermediary when start to finish testing a web application utilizing something like HtmlUnit – you would not need Betamix to catch associations with localhost as that would mean traffic among HtmlUnit and your application was recorded and played back!

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

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 solicitation and reaction bodies are put away as text for most normal printed MIME types. Twofold data for things like pictures is furthermore taken care of anyway isn’t conventional to change the most difficult way possible. Now and again where the text contains non-printable characters then text information will be put away as binary.

PROXY COMPATIBILITY

If you’re using the Betamix 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 127.0.0.1. 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 Betamix intermediary can possibly capture traffic from Apache HttpClient if the customer occurrence is set up to utilize a ProxySelectorRoutePlanner. At the point when Betamix isn’t dynamic this will mean HttpClient traffic will be steered through the default intermediary arranged in Java (assuming 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 gives an execution considered SystemDefaultHttpClient that utilizes the JVM intermediary settings. In a perfect world you can utilize that. Also, Betamix gives a helpful HttpRoutePlanner execution that you can use to arrange examples of other HttpClient types. For instance:

DefaultHttpClient client = new DefaultHttpClient();

BetamixRoutePlanner.configure(client);

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(‘http://groovy.codehaus.org’)

BetamixRoutePlanner.configure(http.client)

HTTPBuilder also includes a HttpURLClient class which needs no special configuration as it uses a java.net.URLConnection 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 Betamix cannot work as seamlessly. You must set the host and port of the Betamix proxy on the HttpClient instance explicitly and Betamix’s ignoreHosts and ignoreLocalhost configuration properties will be completely ignored. For example:

HttpClient client = new HttpClient();

ProxyHost proxy = new ProxyHost(“localhost”, recorder.getProxyPort());

client.getHostConfiguration().setProxyHost(proxy);

WSLite

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

def http = new RESTClient(‘http://freeside.co/betamix’)

def response = http.get(path: ‘/’, proxy: recorder.proxy)

HTTPS

As of version 1.1 the Betamix proxy can handle HTTPS traffic as well as HTTP. Because Betamix needs to be able to read the content of the request and response it is not actually a valid secure proxy. Betamix will only work if the certificate chain is broken. To enable HTTP support, you simply need to set the sslEnabled boolean property on the Recorder instance in your test or via Betamix configuration.

Note, this is not necessary if you are using the BetamixHttpClient 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 Betamix’s HTTPS support:

BetamixHttpsSupport.configure(client);

CONFIGURATION

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

tapeRoot

the base directory where tape files are stored. Defaults to src/test/resources/betamix/tapes.

defaultMode

the default TapeMode applied to an inserted tape when the mode argument is not present on the @Betamix annotation.

ignoreHosts

a list of hosts that will be ignored by the Betamix proxy. Any requests made to these hosts will proceed normally.

ignoreLocalhost

if set to true the Betamix proxy will ignore connections to local addresses. This is equivalent to setting ignoreHosts to [“localhost”, “127.0.0.1”, InetAddress.localHost.hostName, InetAddress.localHost.hostAddress].

When using the Betamix proxy these additional properties are available:

proxyPort

the port the Betamix proxy listens on. Defaults to 5555.

proxyTimeout

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.

sslEnabled

if set to true the Betamix proxy will also intercept HTTPS traffic.

sslSocketFactory

the instance of org.apache.http.conn.ssl.SSLSocketFactory that the Betamix proxy will use to connect to the target when using HTTPS. By default, Betamix 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 BetamixConfig.groovy or betamix.properties somewhere in your classpath it will be picked up by the Recorder class.

Example BetamixConfig.groovy script

betamix {

    tapeRoot = new File(‘src/test/resources/betamix/tapes’)

    useProxy = true

    proxyPort = 5555

    proxyTimeout = 5000

    defaultMode = TapeMode.READ_WRITE

    ignoreHosts = [‘localhost’, ‘127.0.0.1’]

    ignoreLocalhost = false

    sslEnabled = false

    sslSocketFactory = new SSLSocketFactory(myTrustStore)

}

Example betamix.properties file

betamix.tapeRoot=src/test/resources/betamix/tapes

betamix.useProxy=true

betamix.proxyPort=5555

betamix.proxyTimeout=5000

betamix.defaultMode=READ_WRITE

betamix.ignoreHosts=localhost,127.0.0.1

betamix.ignoreLocalhost=false

betamix.sslEnabled=false

CAVEATS

Security

The Betamix 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.

EXAMPLES

Betamix’s GitHub repository includes an example Grails application.

ABOUT

Why “Betamix”?

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

License

Apache Software Licence, Version 2.0

Issues

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

Dependencies

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

  • Groovy 1.8+
  • Apache HttpClient
  • SnakeYAML
  • JUnit 4

In addition, the Betamix proxy requires:

  • Jetty 7

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

Author

  • Rob Fletcher

Contributors

  • Marcin Erdmann
  • Lari Hotari
  • Steve Ims
  • Nobuhiro Sue

Acknowledgements

Betamix 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 KameronBitter and Source Code Pro.

Does Groovy Language Have a Future?

Over the years, technology has been accessible through artificial intelligence that improves translation between programming languages. Researchers continuously enhance this to fix debugs and other... Read More "Does Groovy Language Have a Future?"

Does Groovy Language Have a Future?

Over the years, technology has been accessible through artificial intelligence that improves translation between programming languages. Researchers continuously enhance this to fix debugs and other... Read More "Does Groovy Language Have a Future?"

How to record and replay HTTP traffic

The Internet is the number one reason why we are so connected with each other globally. Our ability to reach out to others through social... Read More "How to record and replay HTTP traffic"

YAML VS YML: WHAT’S THE DIFFERENCE?

YAML is a popular, widely used human-readable format for writing configuration files. It is the abbreviation for YAML Ain’t Markup Language. It is the standard... Read More "YAML VS YML: WHAT’S THE DIFFERENCE?"