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
Section titled “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
Section titled “Installation”uv add --dev quadkit-testingRequires Python >= 3.11.
Minimal working example
Section titled “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.asyncioasync 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.asyncioasync def test_signup_publishes(fake_event_bus) -> None: 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 == 30Optional extras
Section titled “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
Section titled “Public API entry points”Test beds
Section titled “Test beds”from quadkit.testing import AppTestBed, WebTestBedAppTestBed.from_factory(factory, overrides=None)/AppTestBed.from_app(app)— application-level beds.WebTestBed(app_or_provider, raise_server_exceptions=True)withget/post/put/patch/delete,override(Contract, impl)(before boot), andTestResponse—status_code,headers,text,json(property),assert_status,assert_json,assert_json_path,assert_header.quadkit.testing.fixtures.container.ContainerTestFixture— DI-level fixture withmock(),override(),get(),get_optional().quadkit.testing.fixtures.bed.TestEnvironment— programmatic environment builder (use_provider,override,fake,resolve).quadkit.testing.lib.factory.TestDataFactory— deterministiccreate_user(),create_task(),create_message(),create_request().
Fakes (quadkit.testing.fakes)
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “Security”Fakes are in-process and safe to wire into unit suites. Report vulnerabilities privately per SECURITY.md.
Stability
Section titled “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.
- Documentation — oridecon.dev
- Getting started — oridecon.dev/quadkit/getting-started/installation/
- Changelog — https://github.com/dbtinoy-/quadkit/blob/main/CHANGELOG.md
- Issues — https://github.com/dbtinoy-/quadkit/issues
- Security — report privately per SECURITY.md
- Contributing — CONTRIBUTING.md
Apache-2.0 — see LICENSE. “Quadkit” and the Quadkit logo are trademarks of the project — see TRADEMARK.md.