Skip to main content

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 a get_connection_url() or equivalent accessor. Docker Desktop on macOS is detected automatically via ~/.docker/run/docker.sock.
  • qx.testing.MediatorStub — in-memory Mediator replacement. Pre-register handler return values with stub.on(CommandType, result). Asserts that each stubbed command was sent exactly once.
  • qx.testing.RepositoryStub — in-memory Repository[TEntity] backed by a plain dict. Supports add, get, list, save, and soft_delete. No database required.
  • qx.testing.OutboxAssert — queries the real qx_outbox_events table and provides assert_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 DI Container with all singletons initialized.
  • qx.testing.mediator_factory — pytest fixture that creates a Mediator wired to a test container.
  • qx.testing.http_client_factory — pytest fixture that wraps a FastAPI app in a TestClient with 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 NullPool for all test engines that call asyncio.run() in fixtures — asyncpg connections are bound to the event loop that created them and cannot be reused across asyncio.run() boundaries.
  • RepositoryStub raises the same NotFoundError / ConflictError shapes as the real repository so handler tests exercise error paths without a database.
  • MediatorStub.assert_all_sent() can be called in teardown to catch unexpected commands that were sent but not stubbed.

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

qx_testing-1.0.0.tar.gz (8.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

qx_testing-1.0.0-py3-none-any.whl (9.8 kB view details)

Uploaded Python 3

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

Hashes for qx_testing-1.0.0.tar.gz
Algorithm Hash digest
SHA256 733c40ae03d5ecf11cac0bdd4dfa360382e806e036ff1afe7ed8c633c54ec1ad
MD5 114760b53b8be4b7bfab09821059ee87
BLAKE2b-256 8b8a57445bc3e33fb0ecf638ef1664886e612d3c99fbb6f5b88f86d356962130

See more details on using hashes here.

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

Hashes for qx_testing-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5feaadfbf1a1ab51204033fa5dc1622b1452b2b55e3c94f1ea23c94937174269
MD5 c2c6ee775e09d1a03d95cb692e2c585c
BLAKE2b-256 081b1b91fbe85bbfc4f7047a3fab56cfdd855425c38284f437e5747ee99ae332

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page