Skip to content

Warning

This is the documentaion of the older version 0.14.0. See latest for current release.

Developer Interface - Version 0.14.0

Mocking Responses

HTTP Method API

For regular and simple use, use the HTTP method shorthands. See Request API for parameters.

respx.get(url=None, *, name=None, **lookups)

respx.options(...)

respx.head(...)

respx.post(...)

respx.put(...)

respx.patch(...)

respx.delete(...)

Request API

For full control, use the core add method.

respx.add(route, *, name=None)

Parameters:

  • method - str | callable | RequestPattern
    Request HTTP method, or Request callback, to match.
  • url - (optional) str | pattern | tuple (httpcore) | httpx.URL
    Request exact URL, or URL pattern, to match.
  • params - (optional) str | list | dict
    Request URL params to merge with url.
  • status_code - (optional) int - default: 200
    Response status code to mock.
  • headers - (optional) dict
    Response headers to mock.
  • content_type - (optional) str
    Response Content-Type header value to mock.
  • content - (optional) bytes | str | list | dict | callable | exception - default b""
    Response content to mock. - See Response Content.
  • text - (optional) str
    Response text content to mock, with automatic content type header.
  • html - (optional) str
    Response html content to mock, with automatic content type header.
  • json - (optional) str | list | dict
    Response json content to mock, with automatic content type header.
  • pass_through - (optional) bool - default False
    Mark matched request to pass-through to real server, e.g. don't mock.
  • alias - (optional) str
    Name this request pattern. - See Call Statistics.

Matching Requests

Exact URL

To match and mock a request by an exact URL, pass the url parameter as a string.

respx.get("https://foo.bar/", status_code=204)

URL pattern

Instead of matching an exact URL, you can pass a compiled regex to match the request URL.

import httpx
import re
import respx


@respx.mock
def test_something():
    url_pattern = re.compile(r"^https://foo.bar/\w+/$")
    respx.get(url_pattern, content="Baz")
    response = httpx.get("https://foo.bar/baz/")
    assert response.text == "Baz"

Tip

Named groups in the regex pattern will be passed as kwargs to the response content callback, if used.

Base URL

When adding a lot of request patterns sharing the same domain/prefix, you can configure RESPX with a base_url to use as the base when matching URLs.

Like url, the base_url can also be passed as a compiled regex, with optional named groups.

import httpx
import respx


@respx.mock(base_url="https://foo.bar")
async def test_something(respx_mock):
    async with httpx.AsyncClient(base_url="https://foo.bar") as client:
        request = respx_mock.get("/baz/", content="Baz")
        response = await client.get("/baz/")
        assert response.text == "Baz"

Request callback

For full control of what request to match and what response to mock, pass a callback function as the add(method, ...) parameter. The callback's response argument will be pre-populated with any additional response parameters.

import httpx
import respx


def match_and_mock(request, response):
    """
    Return `None` to not match the request.
    Return the `response` to match and mock this request.
    Return the `request` for pass-through behaviour.
    """
    if request.method != "POST":
        return None

    if "X-Auth-Token" not in request.headers:
        response.status_code = 401
    else:
        response.content = "OK"

    return response


@respx.mock
def test_something():
    custom_request = respx.add(match_and_mock, status_code=201)
    respx.get("https://foo.bar/baz/")

    response = httpx.get("https://foo.bar/baz/")
    assert response.status_code == 200
    assert not custom_request.called

    response = httpx.post("https://foo.bar/baz/")
    assert response.status_code == 401
    assert custom_request.called

    response = httpx.post("https://foo.bar/baz/", headers={"X-Auth-Token": "x"})
    assert response.status_code == 201
    assert custom_request.call_count == 2

Repeated patterns

If you mock several responses with the same request pattern, they will be matched in order, and popped til the last one.

import httpx
import respx


@respx.mock
def test_something():
    respx.get("https://foo.bar/baz/123/", status_code=404)
    respx.get("https://foo.bar/baz/123/", content={"id": 123})
    respx.post("https://foo.bar/baz/", status_code=201)

    response = httpx.get("https://foo.bar/baz/123/")
    assert response.status_code == 404  # First match

    response = httpx.post("https://foo.bar/baz/")
    assert response.status_code == 201

    response = httpx.get("https://foo.bar/baz/123/")
    assert response.status_code == 200  # Second match
    assert response.json() == {"id": 123}

Manipulating Existing Patterns

Clearing all existing patterns:

import respx


@respx.mock
def test_something():
    respx.get("https://foo.bar/baz", status_code=404)
    respx.clear()  # no patterns will be matched after this call

Removing and optionally re-using an existing pattern by alias:

import respx


@respx.mock
def test_something():
    respx.get("https://foo.bar/", status_code=404, alias="index")
    request_pattern = respx.pop("index")
    respx.get(request_pattern.url, status_code=200)

Response Content

JSON content

To mock a response with json content, pass a list or a dict.
The Content-Type header will automatically be set to application/json.

import httpx
import respx


@respx.mock
def test_something():
    respx.get("https://foo.bar/baz/123/", content={"id": 123})
    response = httpx.get("https://foo.bar/baz/123/")
    assert response.json() == {"id": 123}

Content callback

If you need dynamic response content, pass a callback function.
When used together with a URL pattern, named groups will be passed as kwargs.

import httpx
import re
import respx


def some_content(request, slug=None):
    """ Return bytes, str, list or a dict. """
    return {"slug": slug}


@respx.mock
def test_something():
    url_pattern = r"^https://foo.bar/(?P<slug>\w+)/$")
    respx.get(url_pattern, content=some_content)

    response = httpx.get("https://foo.bar/apa/")
    assert response.json() == {"slug": "apa"}

Request Error

To simulate a failing request, like a connection error, pass an Exception instance. This is useful when you need to test proper HTTPX error handling in your app.

import httpx
import httpcore
import respx


@respx.mock
def test_something():
    respx.get("https://foo.bar/", content=httpcore.ConnectTimeout())
    response = httpx.get("https://foo.bar/")  # Will raise

Built-in Assertions

RESPX has the following build-in assertion checks:

  • assert_all_mocked
    Asserts that all captured HTTPX requests are mocked. Defaults to True.
  • assert_all_called
    Asserts that all mocked request patterns were called. Defaults to True.

Configure checks by using the respx.mock decorator / context manager with parentheses.

@respx.mock(assert_all_called=False)
def test_something(respx_mock):
    respx_mock.get("https://some.url/")  # OK
    respx_mock.get("https://foo.bar/")

    response = httpx.get("https://foo.bar/")
    assert response.status_code == 200
    assert respx_mock.calls.call_count == 1
with respx.mock(assert_all_mocked=False) as respx_mock:
    response = httpx.get("https://foo.bar/")  # OK
    assert response.status_code == 200
    assert respx_mock.calls.call_count == 1

Without Parentheses

When using the global scope @respx.mock decorator / context manager, assert_all_called is disabled.


Call History

The respx API includes a .calls object, containing captured (request, response) named tuples and MagicMock's bells and whistles, i.e. call_count, assert_called etc.

Retreiving mocked calls

A matched and mocked Call can be retrived from call history, by either unpacking...

request, response = respx.calls.last
request, response = respx.calls[-2]  # by call order

...or by accessing request or response directly...

last_response = respx.calls.last.response

assert respx.calls.last.request.call_count == 1
assert respx.calls.last.response.status_code == 200

Deprecation Warning

As of version 0.14.0, statistics via respx.stats is deprecated, in favour of respx.calls.

Request Pattern calls

Each mocked response request pattern has its own .calls, along with .called and .call_count stats shortcuts.

Example using locally added request pattern:

import httpx
import respx


@respx.mock
def test_something():
    request = respx.post("https://foo.bar/baz/", status_code=201)
    httpx.post("https://foo.bar/baz/")
    assert request.called
    assert request.call_count == 1
    assert request.calls.last.response.status_code == 201
    request.calls.assert_called_once()

Example using globally aliased request pattern:

import httpx
import respx

# Added somewhere outside the test
respx.get("https://foo.bar/", alias="index")

@respx.mock
def test_something():
    httpx.get("https://foo.bar/")
    assert respx.aliases["index"].called
    assert respx.aliases["index"].call_count == 1
    last_index_response = respx.aliases["index"].calls.last.response

Reset stats

To reset stats during a test case, without stop mocking, use respx.reset().

import httpx
import respx


@respx.mock
def test_something():
    respx.post("https://foo.bar/baz/")
    httpx.post("https://foo.bar/baz/")
    assert respx.calls.call_count == 1
    request.calls.assert_called_once()

    respx.reset()
    assert len(respx.calls) == 0
    assert respx.calls.call_count == 0
    respx.calls.assert_not_called()

Examples

Here's a handful example usages of the call stats API.

import httpx
import respx


@respx.mock
def test_something():
    # Mock some calls
    respx.get("https://foo.bar/", alias="index")
    baz_request = respx.post("https://foo.bar/baz/", status_code=201)

    # Make some calls
    httpx.get("https://foo.bar/")
    httpx.post("https://foo.bar/baz/")

    # Assert mocked
    assert respx.aliases["index"].called
    assert respx.aliases["index"].call_count == 1

    assert baz_request.called
    assert baz_request.call_count == 1
    baz_request.calls.assert_called_once()

    # Global stats increased
    assert respx.calls.call_count == 2

    # Assert responses
    assert respx.aliases["index"].calls.last.response.status_code == 200
    assert respx.calls.last.response is baz_request.calls.last.response
    assert respx.calls.last.response.status_code == 201

    # Reset
    respx.reset()
    assert len(respx.calls) == 0
    assert respx.calls.call_count == 0
    respx.calls.assert_not_called()