quadkit-testing
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)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)
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.3 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.3) or a tight range
(>=0.0.3,<0.1.0). Full policy:
stability and compatibility.
Links
- Documentation — https://dbtinoy-.github.io/quadkit/
- Getting started — https://dbtinoy-.github.io/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.
Release files for quadkit-testing 0.0.41
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| quadkit_testing-0.0.41.tar.gz | 190.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| quadkit_testing-0.0.41-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 357.8 kB
Release files / quadkit_testing-0.0.41.tar.gz
| Download URL | quadkit_testing-0.0.41.tar.gz |
|---|---|
| Size | 190.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
822749047fd9ea3d257c81615593311c0c41a1cef32221153b2bfc3e4c8be983
|
|
BLAKE2b-256 checksum How to use checksums |
90f6ea46c3d201761fbf167390b46bf09097c3256113693ce267e02da50cb8b7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.8.14
|
Release files / quadkit_testing-0.0.41-py3-none-any.whl
| Download URL | quadkit_testing-0.0.41-py3-none-any.whl |
|---|---|
| Size | 167.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a6945efa15f8d57ae40c9ca5a57df2d9e905e3cb784d9c510393328378f726fd
|
|
BLAKE2b-256 checksum How to use checksums |
c7f8a9c6df9bfe8327a587bbbafed0d83e58f80f17a1fc8b90ff05ede06b22fa
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.8.14
|