Record Modes

Betamax, like VCR, has four modes that it can use to record cassettes:

  • 'all'
  • 'new_episodes'
  • 'none'
  • 'once'

You can only ever use one record mode. Below are explanations and examples of each record mode. The explanations are blatantly taken from VCR’s own Record Modes documentation.

All

The 'all' record mode will:

  • Record new interactions.
  • Never replay previously recorded interactions.

This can be temporarily used to force VCR to re-record a cassette (i.e., to ensure the responses are not out of date) or can be used when you simply want to log all HTTP requests.

Given our file, examples/record_modes/all/example.py,

import betamax
import requests

CASSETTE_LIBRARY_DIR = 'examples/record_modes/all/'


def main():
    session = requests.Session()
    recorder = betamax.Betamax(
        session, cassette_library_dir=CASSETTE_LIBRARY_DIR
    )

    with recorder.use_cassette('all-example', record='all'):
        session.get('https://httpbin.org/get')
        session.post('https://httpbin.org/post',
                     params={'id': '20'},
                     json={'some-attribute': 'some-value'})
        session.get('https://httpbin.org/get', params={'id': '20'})


if __name__ == '__main__':
    main()

Every time we run it, our cassette (examples/record_modes/all/all-example.json) will be updated with new values.

New Episodes

The 'new_episodes' record mode will:

  • Record new interactions.
  • Replay previously recorded interactions.

It is similar to the 'once' record mode, but will always record new interactions, even if you have an existing recorded one that is similar (but not identical, based on the :match_request_on option).

Given our file, examples/record_modes/new_episodes/example_original.py, with which we have already recorded examples/record_modes/new_episodes/new-episodes-example.json

import betamax
import requests

CASSETTE_LIBRARY_DIR = 'examples/record_modes/new_episodes/'


def main():
    session = requests.Session()
    recorder = betamax.Betamax(
        session, cassette_library_dir=CASSETTE_LIBRARY_DIR
    )

    with recorder.use_cassette('new-episodes-example', record='new_episodes'):
        session.get('https://httpbin.org/get')
        session.post('https://httpbin.org/post',
                     params={'id': '20'},
                     json={'some-attribute': 'some-value'})
        session.get('https://httpbin.org/get', params={'id': '20'})


if __name__ == '__main__':
    main()

If we then run examples/record_modes/new_episodes/example_updated.py

import betamax
import requests

CASSETTE_LIBRARY_DIR = 'examples/record_modes/new_episodes/'


def main():
    session = requests.Session()
    recorder = betamax.Betamax(
        session, cassette_library_dir=CASSETTE_LIBRARY_DIR
    )

    with recorder.use_cassette('new-episodes-example', record='new_episodes'):
        session.get('https://httpbin.org/get')
        session.post('https://httpbin.org/post',
                     params={'id': '20'},
                     json={'some-attribute': 'some-value'})
        session.get('https://httpbin.org/get', params={'id': '20'})
        session.get('https://httpbin.org/get', params={'id': '40'})


if __name__ == '__main__':
    main()

The new request at the end of the file will be added to the cassette without updating the other interactions that were already recorded.

None

The 'none' record mode will:

  • Replay previously recorded interactions.
  • Cause an error to be raised for any new requests.

This is useful when your code makes potentially dangerous HTTP requests. The 'none' record mode guarantees that no new HTTP requests will be made.

Given our file, examples/record_modes/none/example_original.py, with a cassette that already has interactions recorded in examples/record_modes/none/none-example.json

import betamax
import requests

CASSETTE_LIBRARY_DIR = 'examples/record_modes/none/'


def main():
    session = requests.Session()
    recorder = betamax.Betamax(
        session, cassette_library_dir=CASSETTE_LIBRARY_DIR
    )

    with recorder.use_cassette('none-example', record='none'):
        session.get('https://httpbin.org/get')
        session.post('https://httpbin.org/post',
                     params={'id': '20'},
                     json={'some-attribute': 'some-value'})
        session.get('https://httpbin.org/get', params={'id': '20'})


if __name__ == '__main__':
    main()

If we then run examples/record_modes/none/example_updated.py

import betamax
import requests

CASSETTE_LIBRARY_DIR = 'examples/record_modes/none/'


def main():
    session = requests.Session()
    recorder = betamax.Betamax(
        session, cassette_library_dir=CASSETTE_LIBRARY_DIR
    )

    with recorder.use_cassette('none-example', record='none'):
        session.get('https://httpbin.org/get')
        session.post('https://httpbin.org/post',
                     params={'id': '20'},
                     json={'some-attribute': 'some-value'})
        session.get('https://httpbin.org/get', params={'id': '20'})
        session.get('https://httpbin.org/get', params={'id': '40'})


if __name__ == '__main__':
    main()

We’ll see an exception indicating that new interactions were prevented:

Traceback (most recent call last):
  File "examples/record_modes/none/example_updated.py", line 23, in <module>
    main()
  File "examples/record_modes/none/example_updated.py", line 19, in main
    session.get('https://httpbin.org/get', params={'id': '40'})
  File "/usr/local/lib/python2.7/site-packages/requests/sessions.py", line 477, in get
    return self.request('GET', url, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/requests/sessions.py", line 465, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/local/lib/python2.7/site-packages/requests/sessions.py", line 573, in send
    r = adapter.send(request, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/betamax/adapter.py", line 91, in send
    self.cassette))
betamax.exceptions.BetamaxError: A request was made that could not be handled.

A request was made to https://httpbin.org/get?id=40 that could not be found in none-example.

The settings on the cassette are:

    - record_mode: none
    - match_options ['method', 'uri'].

Once

The 'once' record mode will:

  • Replay previously recorded interactions.
  • Record new interactions if there is no cassette file.
  • Cause an error to be raised for new requests if there is a cassette file.

It is similar to the 'new_episodes' record mode, but will prevent new, unexpected requests from being made (i.e. because the request URI changed or whatever).

'once' is the default record mode, used when you do not set one.

If we have a file, examples/record_modes/once/example_original.py,

import betamax
import requests

CASSETTE_LIBRARY_DIR = 'examples/record_modes/once/'


def main():
    session = requests.Session()
    recorder = betamax.Betamax(
        session, cassette_library_dir=CASSETTE_LIBRARY_DIR
    )

    with recorder.use_cassette('once-example', record='once'):
        session.get('https://httpbin.org/get')
        session.post('https://httpbin.org/post',
                     params={'id': '20'},
                     json={'some-attribute': 'some-value'})
        session.get('https://httpbin.org/get', params={'id': '20'})


if __name__ == '__main__':
    main()

And we run it, we’ll see a cassette named examples/record_modes/once/once-example.json has been created.

If we then run examples/record_modes/once/example_updated.py,

import betamax
import requests

CASSETTE_LIBRARY_DIR = 'examples/record_modes/once/'


def main():
    session = requests.Session()
    recorder = betamax.Betamax(
        session, cassette_library_dir=CASSETTE_LIBRARY_DIR
    )

    with recorder.use_cassette('once-example', record='once'):
        session.get('https://httpbin.org/get')
        session.post('https://httpbin.org/post',
                     params={'id': '20'},
                     json={'some-attribute': 'some-value'})
        session.get('https://httpbin.org/get', params={'id': '20'})
        session.get('https://httpbin.org/get', params={'id': '40'})


if __name__ == '__main__':
    main()

We’ll see an exception similar to the one we see when using the 'none' record mode.

Traceback (most recent call last):
  File "examples/record_modes/once/example_updated.py", line 23, in <module>
    main()
  File "examples/record_modes/once/example_updated.py", line 19, in main
    session.get('https://httpbin.org/get', params={'id': '40'})
  File "/usr/local/lib/python2.7/site-packages/requests/sessions.py", line 477, in get
    return self.request('GET', url, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/requests/sessions.py", line 465, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/local/lib/python2.7/site-packages/requests/sessions.py", line 573, in send
    r = adapter.send(request, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/betamax/adapter.py", line 91, in send
    self.cassette))
betamax.exceptions.BetamaxError: A request was made that could not be handled.

A request was made to https://httpbin.org/get?id=40 that could not be found in none-example.

The settings on the cassette are:

    - record_mode: once
    - match_options ['method', 'uri'].