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.
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.1.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.1.0.tar.gz.
File metadata
- Download URL: qx_testing-1.1.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 |
21bf19d4a7d454240b1be119e7397e8d9170aab43a6fe2c0645d10609cf1893c
|
|
| MD5 |
019a04eb107f56e5fcf3e34f09c743ef
|
|
| BLAKE2b-256 |
341201c2dddc74d1327b776e81d4abeb3f9f4938047cfd243a1b77b82706bcd0
|
File details
Details for the file qx_testing-1.1.0-py3-none-any.whl.
File metadata
- Download URL: qx_testing-1.1.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 |
12a1be560fbd2123421606c574f4396d7bbf643babb986ee0ee582739860d4c9
|
|
| MD5 |
e0239cc4e104bbff0e1d399a0d9c07c3
|
|
| BLAKE2b-256 |
6b343cb1cd8d37039c19e816f3b0d75be9cb995c705b85a108a720b7dfd7bc1b
|