Testing APIs offline and fast with vcrpy

Motivation

While testing code against online sources like websites or APIs I often find myself waiting for their responses. Furthermore it feels strange to knock on anyone’s door every couple of seconds just to test what I’m processing locally anyway.

A solution for this issue comes in the form of vcrpy.

Vcrpy frees you from the burden of waiting or even to be online at all. Responses are stored locally in the form of yaml files called cassettes.

In the following post I’ll test against Migros’ mobile plan named M-Budget. Migros is one of Switzerland’s largest wholesale retailers.

The test consists of asserting a word’s appearance on Migros’ website.

Requirements

Python

python --version
Python 3.8.1

Module

pip install --user pytest
pip install --user pytest-vcr

Create the test

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# content of ~/tmp/vhs/test_mbudget.py
import os
import urllib.request
import pytest


@pytest.fixture(scope='module')
def vcr_cassette_dir(request):
    # Put all cassettes in ~/tmp/vhs/{module}/{test}.yaml
    return os.path.join('/home/ra/tmp/vhs', request.module.__name__)

@pytest.mark.vcr()
def test_mbudget():
    response = urllib.request.urlopen('https://selfcare.m-budget.migros.ch/eCare/de/users/sign_in').read()
    assert b'csrf-token' in response

Line 10 says we want our cassetes saved in home/ra/tmp/vhs/test_mbudget

Line 15 asserts the appearance of the word ‘csrf-token’ in the requested website’s source

Run the test

pytest ~/tmp/vhs/test_mbudget.py
============================= test session starts ==============================
platform linux -- Python 3.8.1, pytest-5.3.5, py-1.8.1, pluggy-0.13.1
rootdir: /home/ra
plugins: vcr-1.0.2, pycharm-0.5.0
collected 1 item

../tmp/vhs/test_mbudget.py .                                             [100%]

============================== 1 passed in 11.40s ==============================

Note the time the test needed to pass: 11.40s

Check folders and files

Check the newly created cassettes folder

tree ~/tmp/vhs/
/home/ra/tmp/vhs/
├── __pycache__
│   └── test_mbudget.cpython-38-pytest-5.3.5.pyc
├── test_mbudget
│   └── test_mbudget.yaml
└── test_mbudget.py

2 directories, 3 files

Check the created file

cat ~/tmp/vhs/test_mbudget/test_mbudget.yaml | grep 'csrf-token'
/>\n<meta name=\"csrf-token\" content=\"wtwue7j8wcWPZEw2iI3aJtohGxfN9iSMVlGdbf4kUyT31iupUO2EyJ2i/aZA8z+LHclUsYBbH+HTrScrIaxGyg==\"

Go offline

Let’s disable our network connection

sudo ip link set wlp3s0 down

Run the test again - offline

pytest ~/tmp/vhs/test_mbudget.py
============================= test session starts ==============================
platform linux -- Python 3.8.1, pytest-5.3.5, py-1.8.1, pluggy-0.13.1
rootdir: /home/ra
plugins: vcr-1.0.2, pycharm-0.5.0
collected 1 item

../tmp/vhs/test_mbudget.py .                                             [100%]

============================== 1 passed in 0.05s ===============================

Note how much faster (0.05s) the test ran and we’re offline.

Go back online

Mind to re-enable your network connection

sudo ip link set wlp3s0 up

Conclusion

We were able to reduce waiting from 11 seconds to 0.5 seconds (you’re already doing the ratio math in your head, right?). Furthermore we’re able to continue our development offline and aren’t bothering anyone with countless requests.

Or simply quoting vcrpy’s unique selling propositions:

  • The ability to work offline
  • Completely deterministic tests
  • Increased test execution speed