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.42 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.42) or a tight range (>=0.0.42,<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.

Release files for quadkit-testing 0.0.42

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.42
File Size Uploaded
quadkit_testing-0.0.42.tar.gz 271.5 kB Details

Built distribution (wheel)

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

Total release size: 506.5 kB

Release files / quadkit_testing-0.0.42.tar.gz

Download URL quadkit_testing-0.0.42.tar.gz
Size 271.5 kB
Tags Source
SHA-256 checksum
How to use checksums
29f2b69332cb8f3aedfd25e7ecdc01f4634f4656ab2746ef573b14a591649987
BLAKE2b-256 checksum
How to use checksums
e4d24906a70e3a7948781b1a615400e614760d3a3bef1d1a1ee68bf8f02639c6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.8.14

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

Download URL quadkit_testing-0.0.42-py3-none-any.whl
Size 235.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b083d4d3f9240381f2ab893073c1003f32eb0603538d9b1f8102c80aaae3535c
BLAKE2b-256 checksum
How to use checksums
7b9b9d8c7e761b5f91bc8a2d1e09740c52032e14bb87d8d66cd95c370e500ccd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.8.14

Release history Release notifications | RSS feed

This release

0.0.42 This release

2 release files

0.0.41

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.1

2 release files

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