This package provides a tool for replying HTTP traffic. It is similar
to vcrpy
-- we actually still depend on it -- but with just the
functionality we need.
Essentially, the CassetteDeck
class provides a context manager to be
used in a pytest fixture, which can be included in all tests for which
we want requests to be stored and replayed thereafter.
When entering this context manager, the
aiohttp.ClientSession._request
is replace by our own
handle_request
function. What we do is to try to build the response
from the cassette (there is a per-host cassette, unless a particular
cassette is specified with use_cassette
). If no stored response was
found, we perform the original aiohttp request and store the response
in the cassette. Therefore, next time the same request appears, it
will be replayed.
With CassetteDeck
we can also specify that we don't want requests to
localhost to be recorded with the ignore_localhost=True
parameter.
Moreover, the ignored_hosts
list parameter can be used to extend the
ignored servers. Those will always return the actual server request.
CassetteDeck
allows us to specify a list of mocked services for
which matching requests will be translated into function calls in the
mock object.
An example would be:
class MockedService(BaseService):
def __init__(self, *args, **kwargs):
super().__init__()
self.server = 'http://mocked.service.com'
self.treasure = 'foo'
def matches(self, url):
return url.startswith(self.server)
async def get_the_treasure(self, params=None, data=None, headers={}):
return 200, self.treasure, 'text/plain'
A mocked service must inherit from BaseService
and re-implement the
matches
method. In this case, a request to
http://mocked.service.com/get_the_treasure
will be routed to the
get_the_treasure
method, thus returning whatever we want in the
mock.
By default, CassetteDeck
serializes both the request and the
response inside the cassette. Let's assume a request r1 produces a
certain response that is stored. When a new request r2 comes, it will
replay with a stored response if r2 matches one of the stored requests
in the cassette.
The default matcher checks that the method, url, query parameters, raw body are the same.
However, CassetteDeck
supports specifying custom matchers for
certain tests with the custom_matchers
argument. This way, one can
implement non-standard logic to match requests, for instance, using
specific parameters on the headers.
An example would be:
def CustomMatcher(r1, r2):
"""This matcher will return the same response for all requests that
have 'hello' in the request body.
"""
if 'hello' not in r1.body.decode():
return False
if 'hello' not in r2.body.decode():
return False
return True
Thus can be specified in the custom_matchers
list along with other
matchers:
from cassettedeck import CassetteDeck
from vcr.matchers import uri
from vcr.matchers import method
ctd = CassetteDeck()
with ctd.use_cassette(
'my_cassette',
custom_matchers=[
CustomMatcher,
uri,
method,
])