Qx testing helpers: testcontainers, fixtures, mediator/repository doubles, outbox assertions
Project description
qx-testing
Testing helpers for Qx services — testcontainers, mediator and repository doubles, outbox assertions, and pytest fixture factories.
What lives here
qx.testing.postgres_container/redis_container/nats_container— context managers that start Docker containers via testcontainers. Each yields the running container with aget_connection_url()or equivalent accessor. Docker Desktop on macOS is detected automatically via~/.docker/run/docker.sock.qx.testing.MediatorStub— in-memoryMediatorreplacement. Pre-register handler return values withstub.on(CommandType, result). Asserts that each stubbed command was sent exactly once.qx.testing.RepositoryStub— in-memoryRepository[TEntity]backed by a plaindict. Supportsadd,get,list,save, andsoft_delete. No database required.qx.testing.OutboxAssert— queries the realqx_outbox_eventstable and providesassert_event_published(event_name, where={...}). Used in integration tests to verify that the transactional outbox was populated correctly.qx.testing.container_factory— pytest fixture that builds and tears down a DIContainerwith all singletons initialized.qx.testing.mediator_factory— pytest fixture that creates aMediatorwired to a test container.qx.testing.http_client_factory— pytest fixture that wraps a FastAPI app in aTestClientwith a fresh Prometheus registry per test.
Usage
Integration test with a real Postgres container
import pytest
from qx.testing.containers import postgres_container
from qx.testing.assertions import OutboxAssert
@pytest.fixture(scope="session")
def db_url():
with postgres_container() as pg:
yield pg.get_connection_url()
def test_create_user_writes_outbox(client, outbox: OutboxAssert):
client.post("/v1/users", json={"email": "a@b.com", "name": "Ada"})
import asyncio
asyncio.run(
outbox.assert_event_published(
"identity.user.registered",
where={"email": "a@b.com"},
)
)
Unit test with doubles
from qx.testing import MediatorStub, RepositoryStub
from qx.core import Result
async def test_create_user_handler():
users = RepositoryStub(User)
uow = FakeUnitOfWork(users)
handler = CreateUserHandler(uow)
result = await handler.handle(CreateUserCommand(email="a@b.com", name="Ada"))
assert result.is_success
assert len(users.all()) == 1
Design rules
- Use
NullPoolfor all test engines that callasyncio.run()in fixtures — asyncpg connections are bound to the event loop that created them and cannot be reused acrossasyncio.run()boundaries. RepositoryStubraises the sameNotFoundError/ConflictErrorshapes as the real repository so handler tests exercise error paths without a database.MediatorStub.assert_all_sent()can be called inteardownto catch unexpected commands that were sent but not stubbed.
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.
Source Distribution
qx_testing-1.0.0.tar.gz
(8.5 kB
view details)
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 qx_testing-1.0.0.tar.gz.
File metadata
- Download URL: qx_testing-1.0.0.tar.gz
- Upload date:
- Size: 8.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
733c40ae03d5ecf11cac0bdd4dfa360382e806e036ff1afe7ed8c633c54ec1ad
|
|
| MD5 |
114760b53b8be4b7bfab09821059ee87
|
|
| BLAKE2b-256 |
8b8a57445bc3e33fb0ecf638ef1664886e612d3c99fbb6f5b88f86d356962130
|
File details
Details for the file qx_testing-1.0.0-py3-none-any.whl.
File metadata
- Download URL: qx_testing-1.0.0-py3-none-any.whl
- Upload date:
- Size: 9.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5feaadfbf1a1ab51204033fa5dc1622b1452b2b55e3c94f1ea23c94937174269
|
|
| MD5 |
c2c6ee775e09d1a03d95cb692e2c585c
|
|
| BLAKE2b-256 |
081b1b91fbe85bbfc4f7047a3fab56cfdd855425c38284f437e5747ee99ae332
|