A utility for mocking out the Python HTTPX library.
Project description
RESPX
A utility for mocking out the Python HTTPX library.
Usage
For starters, you need to mock HTTPX
, by using the RESPX
decorator or context managers.
Decorator
import httpx import respx @respx.mock def test_something(): request = respx.post("https://foo.bar/baz/", status_code=201) response = httpx.post("https://foo.bar/baz/") assert request.called assert response.status_code == 201 @respx.mock(assert_all_mocked=False) def test_something(httpx_mock): response = httpx.post("https://foo.bar/baz/") assert response.status_code == 200
Context Manager
import httpx import respx with respx.mock: request = respx.get("https://foo.bar/", content={"foo": "bar"}) response = httpx.get("https://foo.bar/") assert request.called assert response.json() == {"foo": "bar"} with respx.mock(assert_all_called=False) as httpx_mock: httpx_mock.get("https://ham.spam/") request = httpx_mock.get("https://foo.bar/", content={"foo": "bar"}) response = httpx.get("https://foo.bar/") assert request.called assert response.json() == {"foo": "bar"}
NOTE: You can also start and stop mocking
HTTPX
manually, by callingrespx.start()
andrespx.stop()
Mocking responses
To mock a response, define the request pattern to match and the response details to return.
For regular and simple use, use the HTTP method shorthands:
respx.get
(url=None, status_code=None, content=None, content_type=None, headers=None, pass_through=False, alias=None) -> RequestPattern
respx.options
(url=None, ...)
respx.head
(url=None, ...)
respx.post
(url=None, ...)
respx.put
(url=None, ...)
respx.patch
(url=None, ...)
respx.delete
(url=None, ...)
For advanced use:
respx.request
(method, url=None, status_code=None, content=None, content_type=None, headers=None, pass_through=False, alias=None) -> RequestPattern
Parameters
- method - str | callable
- Request HTTP method to match - GET, OPTIONS, HEAD, POST, PUT, PATCH or DELETE.
- Request match callback. See Custom request matching.
- url - (optional) str | pattern
- Request URL exact string to match.
- Request URL pattern to match. See URL pattern matching.
- status_code - (optional) int
Response status code. [Default: 200]- content - (optional) bytes | str | list | dict | callable | exception
Response content. [Default: b""] - See JSON content, Content callback.- content_type - (optional) str
Response Content-Type header value. [Default: text/plain]- headers - (optional) dict
Response headers.- pass_through - (optional) bool
Mark request to pass-through to real server, e.g. don't mock. [Default: False]- alias - (optional) str
Name this request pattern. - See Call stats.
URL pattern matching
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(): respx.get(re.compile(r"^https://foo.bar/.*$"), content="Baz") response = httpx.get("https://foo.bar/baz/") assert response.text == "Baz"
NOTE: Named groups in the pattern will be passed as
kwargs
to content callback, if used.
JSON content
To mock a response with json content, pass a list
or dict
. The Content-Type
header will 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, use a callback function as the content
parameter.
import httpx import re import respx def baz_content(request, pk=None): """ If a URL pattern were used, named groups will be passed as optional kwargs. Return bytes, str, list or a dict. """ return {"id": int(pk)} @respx.mock def test_something(): respx.get(re.compile(r"^https://foo.bar/baz/(?P<pk>\d+)/$"), content=baz_content) response = httpx.get("https://foo.bar/baz/123/") assert response.json() == {"id": 123}
Mock a request exception
To simulate a request problem, like a connection error, pass an Exception
instance as the content
parameter.
import httpx import respx @respx.mock def test_something(): respx.get("https://foo.bar/", content=httpx.ConnectTimeout()) response = httpx.get("https://foo.bar/") # Will raise
Custom request matching
For full control of what to match and what response to mock, pass a callback function as the method
parameter.
import httpx import respx def custom_matcher(request, response): """ Response object is populated with any given response parameters from the respx.request(...) call. Return None to not match. Return the response for a match and to 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.request(custom_matcher, 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": "token"}) 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}
Built-in assertions
RESPX has the following build-in assertion checks:
- assert_all_mocked
Asserts that all capturedHTTPX
requests are mocked.- assert_all_called
Asserts that all mocked request patterns were called.
When using the high-level respx.mock
decorator/manager, assert_all_called
is disabled.
When using the low-level respx.HTTPXMock
context manager, both checks is enabled by default.
with respx.HTTPXMock(assert_all_called=False, assert_all_mocked=False) as respx_mock: response = httpx.get("https://foo.bar/") # Will not raise AssersionError, but instead auto mock. assert response.status_code == 200 assert respx_mock.stats.call_count == 1
Call stats
The global respx
api has a .calls
list, containing captured (request
, response
) tuples. On top of that there's also a MagicMock .stats
object with all its bells and whistles, i.e. call_count
, assert_called_once
etc.
Request patterns has their own .calls
and .stats
, along with shortcuts to stats.called
and .call_count
.
import httpx import respx @respx.mock def test_something(): respx.get("https://foo.bar/", alias="index") # Aliased request pattern create_request = respx.post("https://foo.bar/baz/") put_request = respx.put("https://foo.bar/baz/123/", status_code=202) httpx.get("https://foo.bar/") index_request = respx.aliases["index"] # Alias assert index_request.called assert index_request.call_count == 1 httpx.post("https://foo.bar/baz/") assert create_request.called assert create_request.call_count == 1 httpx.put("https://foo.bar/baz/123/") assert put_request.called assert put_request.call_count == 1 assert respx.stats.call_count == 3 request, response = respx.calls[-1] assert request.method == "PUT" assert response.status_code == 202
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Filename, size | File type | Python version | Upload date | Hashes |
---|---|---|---|---|
Filename, size respx-0.7.4-py3-none-any.whl (9.3 kB) | File type Wheel | Python version py3 | Upload date | Hashes View |
Filename, size respx-0.7.4.tar.gz (12.0 kB) | File type Source | Python version None | Upload date | Hashes View |