Request Matching

Whilst it is preferable to provide the whole URI to requests_mock.Adapter.register_uri() it is possible to just specify components.

The examples in this file are loaded with:

>>> import requests
>>> import requests_mock
>>> adapter = requests_mock.Adapter()
>>> session = requests.Session()
>>> session.mount('mock://', adapter)

Note

The examples within use this syntax because request matching is a function of the adapter and not the mocker. All the same arguments can be provided to the mocker if that is how you use requests_mock within your project, and use the

mock.get(url, ...)

form in place of the given:

adapter.register_uri('GET', url, ...)

If you are not familiar with requests’ adapters (see Adapter Usage), prefer the mocker approach (see Using the Mocker).

Note

By default all matching is case insensitive. This can be adjusted by passing case_sensitive=True when creating a mocker or adapter, or globally by doing:

requests_mock.mock.case_sensitive = True

for more, see: Case Insensitivity

Simple

The most simple way to match a request is to register the URL and method that will be requested with a textual response. When a request is made that goes through the mocker this response will be retrieved.

.. >>> adapter.register_uri('GET', 'mock://test.com/path', text='resp')
.. >>> session.get('mock://test.com/path').text
.. 'resp'

Path Matching

You can specify a protocol-less path:

.. >>> adapter.register_uri('GET', '//test.com/', text='resp')
.. >>> session.get('mock://test.com/').text
.. 'resp'

or you can specify just a path:

.. >>> adapter.register_uri('GET', '/path', text='resp')
.. >>> session.get('mock://test.com/path').text
.. 'resp'
.. >>> session.get('mock://another.com/path').text
.. 'resp'

Query Strings

Query strings provided to a register will match so long as at least those provided form part of the request.

>>> adapter.register_uri('GET', '/7?a=1', text='resp')
>>> session.get('mock://test.com/7?a=1&b=2').text
'resp'

We can also match an empty query string.

>>> adapter.register_uri('GET', '/7?a', text='resp')
>>> session.get('mock://test.com/7?a').text
'resp'

If any part of the query string is wrong then it will not match.

>>> session.get('mock://test.com/7?a=3')
Traceback (most recent call last):
   ...
requests_mock.exceptions.NoMockAddress: No mock address: GET mock://test.com/7?a=3

This can be a problem in certain situations, so if you wish to match only the complete query string there is a flag complete_qs:

>>> adapter.register_uri('GET', '/8?a=1', complete_qs=True, text='resp')
>>> session.get('mock://test.com/8?a=1&b=2')
Traceback (most recent call last):
   ...
requests_mock.exceptions.NoMockAddress: No mock address: GET mock://test.com/8?a=1&b=2

Matching ANY

There is a special symbol at requests_mock.ANY which acts as the wildcard to match anything. It can be used as a replace for the method and/or the URL.

>>> adapter.register_uri(requests_mock.ANY, 'mock://test.com/8', text='resp')
>>> session.get('mock://test.com/8').text
'resp'
>>> session.post('mock://test.com/8').text
'resp'
>>> adapter.register_uri(requests_mock.ANY, requests_mock.ANY, text='resp')
>>> session.get('mock://whatever/you/like').text
'resp'
>>> session.post('mock://whatever/you/like').text
'resp'

Regular Expressions

URLs can be specified with a regular expression using the python re module. To use this you should pass an object created by re.compile().

The URL is then matched using re.regex.search() which means that it will match any component of the url, so if you want to match the start of a URL you will have to anchor it.

.. >>> import re
.. >>> matcher = re.compile('tester.com/a')
.. >>> adapter.register_uri('GET', matcher, text='resp')
.. >>> session.get('mock://www.tester.com/a/b').text
.. 'resp'

If you use regular expression matching then requests-mock can’t do its normal query string or path-only matching. That will need to be part of the expression.

Request Headers

A dictionary of headers can be supplied such that the request will only match if the available headers also match. Only the headers that are provided need match, any additional headers will be ignored.

>>> adapter.register_uri('POST', 'mock://test.com/headers', request_headers={'key': 'val'}, text='resp')
>>> session.post('mock://test.com/headers', headers={'key': 'val', 'another': 'header'}).text
'resp'
>>> resp = session.post('mock://test.com/headers')
Traceback (most recent call last):
   ...
requests_mock.exceptions.NoMockAddress: No mock address: POST mock://test.com/headers

Additional Matchers

As distinct from Custom Matching below, we can add an additional matcher callback that lets us do more dynamic matching in addition to the standard options. This is handled by a callback function that takes the request as a parameter:

>>> def match_request_text(request):
...     # request.text may be None, or '' prevents a TypeError.
...     return 'hello' in (request.text or '')
...
>>> adapter.register_uri('POST', 'mock://test.com/additional', additional_matcher=match_request_text, text='resp')
>>> session.post('mock://test.com/additional', data='hello world').text
'resp'
>>> resp = session.post('mock://test.com/additional', data='goodbye world')
Traceback (most recent call last):
   ...
requests_mock.exceptions.NoMockAddress: No mock address: POST mock://test.com/additional

Using this mechanism lets you do custom handling such as parsing yaml or XML structures and matching on features of that data or anything else that is not directly handled via the provided matchers rather than build in every possible option to requests_mock.

Custom Matching

Internally, calling register_uri() creates a matcher object for you and adds it to the list of matchers to check against.

A matcher is any callable that takes a requests.Request and returns a requests.Response on a successful match or None if it does not handle the request.

If you need more flexibility than provided by register_uri() then you can add your own matcher to the Adapter. Custom matchers can be used in conjunction with the inbuilt matchers. If a matcher returns None then the request will be passed to the next matcher as with using register_uri().

>>> def custom_matcher(request):
...     if request.path_url == '/test':
...         resp = requests.Response()
...         resp.status_code = 200
...         return resp
...     return None
...
>>> adapter.add_matcher(custom_matcher)
>>> session.get('mock://test.com/test').status_code
200
>>> session.get('mock://test.com/other')
Traceback (most recent call last):
   ...
requests_mock.exceptions.NoMockAddress: No mock address: POST mock://test.com/other