# Testing β€” Smoke-First, Contract-First Everything `openapi-first` ships is tested against **real specs, real handlers, and real HTTP** through FastAPI's `TestClient` and the bundled templates. There are no fakes of the library itself. --- ## πŸ§ͺ 1. Suite Overview ``` tests/ β”œβ”€β”€ conftest.py # fixtures: spec_file, routes_module, app, client β”œβ”€β”€ test_app.py # OpenAPIFirstApp: routes served, overrides, fail-fast β”œβ”€β”€ test_binder.py # bind_routes: opId resolution + missing-handler failures β”œβ”€β”€ test_loader.py # load_openapi: json/yaml, env resolution, validation └── test_client.py # OpenAPIClient: opIdβ†’callable, params, error cases ``` Run with: ```bash pytest # 23 tests, zero mocks of the library pytest -q pytest tests/test_loader.py ``` --- ## πŸ—οΈ 2. The Fixture Pattern ```python # conftest.py (abridged) @pytest.fixture def spec_file(tmp_path): path = tmp_path / "openapi.json" path.write_text(json.dumps(SPEC), encoding="utf-8") return str(path) @pytest.fixture def app(spec_file): return OpenAPIFirstApp( openapi_path=spec_file, routes_module=routes_module(), ) @pytest.fixture def client(spec_file): return OpenAPIClient(json.loads(Path(spec_file).read_text())) ``` --- ## πŸ§ͺ 3. What's Actually Asserted ### 3.1 App-level smoke ```python def test_app_routes_served(spec_file): app = OpenAPIFirstApp(openapi_path=spec_file, routes_module=routes) client = TestClient(app) assert client.get("/health").json() == {"status": "ok"} ``` ### 3.2 Fail-fast (the heart) ```python def test_app_missing_handler_fails_at_startup(spec_file): with pytest.raises(MissingOperationHandler): OpenAPIFirstApp(openapi_path=spec_file, routes_module=empty_routes) ``` ### 3.3 Loader validation ```python def test_loader_invalid_spec_fails(): with pytest.raises(OpenAPISpecLoadError): load_openapi("broken.yaml") ``` ### 3.4 Client contract drift ```python def test_client_duplicate_operation_id_raises(): with pytest.raises(OpenAPIClientError): OpenAPIClient(dup_spec) ``` --- ## πŸ›οΈ 4. Testing the Templates Each bundled template ships its own test + in-memory store, so you get a runnable contract test the moment you scaffold: ```bash openapi-first scaffold crud_app my-service cd my-service pytest -q ``` `test_crud_app.py` / `test_model_app.py` / `test_vet_app.py` exercise the full CRUD surface through `TestClient` β€” including `201`/`204` status codes, 404s, and (in `vet_app`) SSE streaming via `StreamingResponse`. --- ## πŸ•ΉοΈ 5. Quality Gates (CI) The same gates the library itself must pass are what keep the docs honest: | Gate | Purpose | |------|---------| | `black --check` | formatting parity | | `ruff check` | lint hygiene | | `mypy` | type safety (strict, `--disable-error-code` where intentional) | | `pytest` | 23 tests, green | | coverage | tracked via pytest-cov (HTML + XML + term) | Run the whole gate locally: ```bash black --check openapi_first tests ruff check openapi_first tests mypy openapi_first pytest ``` --- ## πŸ’‘ 6. Testing Tips - **Start from startup:** assert `OpenAPIFirstApp(...)` *raises* for the broken contracts β€” those are your most valuable tests - **Client ↔ server:** smoke a client against the same spec the app was built from β€” one spec, two sides, zero drift - **Templates are scaffolds:** their tests are copyable starting points, not canonical suites - **No mocking of the library:** exercise loader β†’ binder β†’ app β†’ client as a real pipeline --- ## Related - [01 – Overview](01_overview.md) Β· [02 – Components](02_components.md) Β· [04 – Error Handling](06_error_handling.md)