Warning
This is the documentation 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 built-in assertion checks:
- assert_all_mocked
Asserts that all capturedHTTPX
requests are mocked. Defaults toTrue
.- assert_all_called
Asserts that all mocked request patterns were called. Defaults toTrue
.
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.
Retrieving mocked calls
A matched and mocked Call
can be retrieved 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()