Skip to main content

quadkit-testing

Quadkit

PyPI Python License

Test harnesses, fakes, and fixtures for Quadkit applications: boot the real application in-process, substitute bindings instead of mocking import sites, and assert on responses with helpers that say what failed.

For anyone writing tests against Quadkit applications — and for tooling that needs drop-in implementations of the framework's protocols.

The quadkit family

Package Role
quadkit-contracts zero-dependency protocols, types, exception hierarchy
quadkit the framework core — DI container, modules, config, logging, Result
quadkit-web ASGI layer — controllers, routing, middleware, OpenAPI docs
quadkit-cli project scaffolding and code generators
quadkit-testing in-process test beds, fakes, fixtures

Installation

uv add --dev quadkit-testing

Requires Python >= 3.11.

Minimal working example

Test an HTTP route with the real application, in-process:

import pytest

from quadkit.testing import WebTestBed

from my_app import create_app


@pytest.mark.asyncio
async def test_hello() -> None:
    async with WebTestBed(create_app()) as bed:
        response = bed.get("/hello", params={"name": "quadkit"})
        response.assert_status(200)
        assert response.json == {"message": "hello, quadkit"}

Or test services directly, with binding overrides:

async def test_service() -> None:
    from quadkit.testing import AppTestBed

    async with AppTestBed.from_factory(
        create_app, overrides={Cache: FakeCache()}
    ) as bed:
        service = await bed.app.container.resolve(UserService)

A pytest plugin registers automatically via entry points — no conftest.py wiring — and provides auto-registered fixtures including fake_cache, fake_event_bus, fake_logger, fake_clock, fake_command_bus, fake_query_bus, fake_unit_of_work, fake_metrics, fake_config, fake_state_store, test_bed, test_container, and test_data:

import pytest

from quadkit.contracts.domain.events import DomainEvent


class UserCreated(DomainEvent):
    user_id: str
    email: str


@pytest.mark.asyncio
async def test_signup_publishes(fake_event_bus) -> None:
    await fake_event_bus.publish(UserCreated(user_id="1", email="a@b.c"))
    fake_event_bus.assert_published(UserCreated, user_id="1")


def test_trial_expiry_is_deterministic(fake_clock) -> None:
    t0 = fake_clock.now()
    fake_clock.advance(30 * 24 * 3600)
    assert (fake_clock.now() - t0).days == 30

Optional extras

Extra Contents
[web] httpx + Starlette — the WebTestBed client transport
[db] aiosqlite, asyncpg — drivers for your async DB test suites
[integration] service clients for integration suites (Redis, MongoDB, Kafka, Elasticsearch, Neo4j, Qdrant, PostgreSQL, SQLite)
[dev] ruff, mypy, black

Public API entry points

Test beds

from quadkit.testing import AppTestBed, WebTestBed
  • AppTestBed.from_factory(factory, overrides=None) / AppTestBed.from_app(app) — application-level beds.
  • WebTestBed(app_or_provider, raise_server_exceptions=True) with get/post/put/patch/delete, override(Contract, impl) (before boot), and TestResponse — status_code, headers, text, json (property), assert_status, assert_json, assert_json_path, assert_header.
  • quadkit.testing.fixtures.container.ContainerTestFixture — DI-level fixture with mock(), override(), get(), get_optional().
  • quadkit.testing.fixtures.bed.TestEnvironment — programmatic environment builder (use_provider, override, fake, resolve).
  • quadkit.testing.lib.factory.TestDataFactory — deterministic create_user(), create_task(), create_message(), create_request().

Fakes (quadkit.testing.fakes)

All in-process, async-native, implementing the same contracts as the real services:

Class Covers
FakeCache / FakeStateStore cache and state storage
FakeEventBus in-process events with assert_published(), published_of_type(), assert_events_in_order() and friends
FakeCommandBus / FakeQueryBus command / query dispatch
FakeUnitOfWork unit-of-work context
FakeClock (+ Clock, SystemClock) deterministic time
FakeConfig config overrides
FakeLogger (+ LogEntry) structlog-compatible sink
FakeMetricsCollector / FakeResourceUnitTracker metrics / resource tracking
FakeRedisClient Redis-protocol client
FakeAuditLogger audit records
FakeTracer / FakeSpan tracing

The published wheel carries the ai, db and web test clients and beds plus the fakes and fixtures that need no private package. The auth, cache, events, search, storage, tasks, ui test clients, the AI/DB/task fixture modules, the secrets fake and IntegrationEnvironment stay in this repository until their packages publish; asking the published wheel for one raises AttributeError naming the module.

Configuration

None required — the pytest plugin self-registers. Mark suites for external services and gate them yourself (e.g. uv run pytest -m "not integration").

Error handling

Test beds surface failures, they don't hide them: with raise_server_exceptions=True (the default) unexpected exceptions re-raise into your test with their original traceback; HTTP-expected failures assert on the response instead.

Testing

Ironically self-hosted: this package's public tests are among the suite the release executes from the exported tree against the built wheels.

Security

Fakes are in-process and safe to wire into unit suites. Report vulnerabilities privately per SECURITY.md.

Stability

Version 0.0.43 in the 0.x series, released in lockstep with the other four distributions; APIs may change between minor versions until 1.0 — pin an exact version (quadkit-testing==0.0.43) or a tight range (>=0.0.43,<0.1.0). Full policy: stability and compatibility.

Apache-2.0 — see LICENSE. "Quadkit" and the Quadkit logo are trademarks of the project — see TRADEMARK.md.

Metadata

Release files for quadkit-testing 0.0.43

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for quadkit-testing 0.0.43
File Size Uploaded Provenance
quadkit_testing-0.0.43.tar.gz 195.6 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for quadkit-testing 0.0.43
File Interpreter ABI Platform
quadkit_testing-0.0.43-py3-none-any.whl Python 3 none any Details

Total release size: 366.0 kB

Release files / quadkit_testing-0.0.43.tar.gz

Download URL quadkit_testing-0.0.43.tar.gz
Size 195.6 kB
Tags Source
SHA-256 checksum
How to use checksums
eef32e6a64a0fe5bf6482dc34068324005153d97c2ab2d5ed4454228e12e5b8e
BLAKE2b-256 checksum
How to use checksums
2e4744d9b32bd955a7e71d5cb5094a1afe0e0c02ae300232a1a9dc53d8b5c1d5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.8.14

Release files / quadkit_testing-0.0.43-py3-none-any.whl

Download URL quadkit_testing-0.0.43-py3-none-any.whl
Size 170.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1673ca2b9ccc182897f19eebb8ffbd73849ad94c84fb685da48c3e28bdd1aea0
BLAKE2b-256 checksum
How to use checksums
82423a6dd3990498c41273743f3bce051e610e75cc8dd04a8a8839a603fe3e42
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.8.14

Security for quadkit-testing 0.0.43

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page