Testing utility decorators for mocking functions, variables, and HTTP endpoints
Project description
pymocks
Testing utility decorators and context managers for mocking functions, variables, and HTTP endpoints in Python.
Works with both synchronous and asynchronous test functions. Sync/async is detected automatically. All mocking utilities can be used as decorators or context managers.
Installation
pip install pymocks
Or with uv:
uv add pymocks
Usage
Mocking Functions and Variables
Use Mock with with_mock to monkeypatch a module attribute for the duration of a test. Works as a decorator or context manager:
import my_module
from pymocks import Mock, with_mock
# Mock a function — the replacement must have the same signature
def fake_function(x: int, y: str) -> bool:
return True
mock = Mock(
module_where_used=my_module,
current_value=my_module.some_function,
new_value=fake_function,
)
@with_mock(mock)
def test_with_mocked_function():
result = my_module.some_function(1, "a")
assert result is True
# Mock a variable — the replacement must have the same type
var_mock = Mock(
module_where_used=my_module,
current_value=my_module.API_URL,
new_value="https://mock.example.com",
)
@with_mock(var_mock)
def test_with_mocked_variable():
assert my_module.API_URL == "https://mock.example.com"
The same works for async tests:
@with_mock(mock)
async def test_async_with_mock():
result = my_module.some_function(1, "a")
assert result is True
Or use it as a context manager for more flexible scoping:
def test_with_context_manager():
with with_mock(mock):
result = my_module.some_function(1, "a")
assert result is True
# mock is reverted here
async def test_async_with_context_manager():
async with with_mock(mock):
result = my_module.some_function(1, "a")
assert result is True
Mocking Classes
Use Mock to replace a class with a subclass. The replacement must be a subclass of the original:
import my_module
from pymocks import Mock, with_mock
class FakeService(my_module.Service):
def fetch(self) -> str:
return "fake data"
mock = Mock(
module_where_used=my_module,
current_value=my_module.Service,
new_value=FakeService,
)
@with_mock(mock)
def test_with_mocked_class():
svc = my_module.Service()
assert svc.fetch() == "fake data"
Replacing a class with an unrelated class raises TypeError:
class Unrelated:
pass
# Raises TypeError — Unrelated is not a subclass of Service
Mock(
module_where_used=my_module,
current_value=my_module.Service,
new_value=Unrelated,
)
Signature and Type Validation
Mock validates compatibility between current_value and new_value at construction time:
- Callables: signatures must match exactly (parameter count, names, kinds, annotations, and return annotation)
- Classes:
new_valuemust be a subclass ofcurrent_value - Non-callables:
type(current_value)must be the same astype(new_value) - Mixed: replacing a callable with a non-callable (or vice versa) raises
TypeError
def original(x: int) -> str:
return str(x)
# Signature mismatch — raises TypeError immediately
Mock(
module_where_used=my_module,
current_value=original,
new_value=lambda x: str(x), # missing annotations
)
# Type mismatch — raises TypeError immediately
Mock(
module_where_used=my_module,
current_value="a string",
new_value=42,
)
Mocking HTTP Endpoints
Use MockEndpoint with with_endpoints to mock HTTP calls via aioresponses. Works as a decorator or context manager:
import aiohttp
from pymocks import MockEndpoint, with_endpoints
endpoints = (
MockEndpoint(
url="https://api.example.com/users",
method="GET",
json_response={"users": [{"id": 1, "name": "Alice"}]},
),
MockEndpoint(
url="https://api.example.com/users",
method="POST",
json_response={"id": 2, "name": "Bob"},
),
)
# As a decorator
@with_endpoints(endpoints)
async def test_api_calls():
async with aiohttp.ClientSession() as session:
async with session.get("https://api.example.com/users") as resp:
data = await resp.json()
assert len(data["users"]) == 1
# As a context manager
async def test_api_calls_ctx():
async with with_endpoints(endpoints):
async with aiohttp.ClientSession() as session:
async with session.get("https://api.example.com/users") as resp:
data = await resp.json()
assert len(data["users"]) == 1
API Reference
Mock[T_mocked]
A dataclass that defines a monkeypatch specification. Validates compatibility on construction.
| Field | Type | Description |
|---|---|---|
module_where_used |
ModuleType |
The module containing the attribute to patch |
current_value |
T_mocked |
The current value (used to find its name) |
new_value |
T_mocked |
The replacement value during the test |
MockEndpoint
A frozen dataclass defining an HTTP endpoint mock.
| Field | Type | Description |
|---|---|---|
url |
str |
The URL to mock |
method |
Literal["GET", "POST", "PUT", "DELETE"] |
HTTP method |
json_response |
dict[str, JsonValue] | None |
JSON response body (optional) |
body |
str | None |
Raw string body (optional) |
with_mock(mock) / with_endpoints(endpoints)
Both can be used as decorators or context managers (sync and async):
# Decorator
@with_mock(mock)
def test_decorated(): ...
# Sync context manager
with with_mock(mock):
...
# Async context manager
async with with_mock(mock):
...
When used as decorators, sync/async is detected automatically.
Requirements
- Python >= 3.12
- pytest
- aioresponses
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file py_mocks-0.2.0.tar.gz.
File metadata
- Download URL: py_mocks-0.2.0.tar.gz
- Upload date:
- Size: 51.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
073bbd33d16d21a7440e753ee60f3a15148897900af51f74cd06a4aae05e894c
|
|
| MD5 |
e8f7a305b8b0ceec24c39a0c2a654d92
|
|
| BLAKE2b-256 |
3f5c7703167b53e80d949a7c584ace4a0074dcf10ecebf3b4924c7a4f94dfa18
|
File details
Details for the file py_mocks-0.2.0-py3-none-any.whl.
File metadata
- Download URL: py_mocks-0.2.0-py3-none-any.whl
- Upload date:
- Size: 6.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fa7a2030fa9f259495f984017a7f00d269effba0eeded6c147a248a09d6a5e85
|
|
| MD5 |
30fa9959c06ab687c8c6518b457b578e
|
|
| BLAKE2b-256 |
eab57e1c6857d1f72b79b82033b44f86b9e6140e9aefcd02088d38fa1998a907
|