Files
prompts/skills/pytest-scaffolding/SKILL.md
T
John Lancaster c8906cef7b reorg
2026-06-16 23:39:45 -05:00

5.6 KiB

name, description, argument-hint
name description argument-hint
pytest-scaffolding Scaffold a maintainable, hierarchical pytest suite for core functionality first, then extend safely. Use when setting up tests, organizing fixtures by dependency, mirroring src structure in tests, or enforcing fast-by-default test runs. Target scope (for example: app/services/job, app/ai, or full repo)

Pytest Scaffolding

Create test scaffolding that is:

  • Hierarchical: test layout roughly mirrors source layout.
  • Fast by default: most tests run in under a second total for core units.
  • Dependency-aware: slow/external dependencies are isolated behind markers and fixture scope.
  • Extensible: minimal initial skeleton supports adding detailed tests later without refactors.

This repository currently uses:

  • uv run pytest as the canonical test invocation.
  • pyproject.toml pytest config under [tool.pytest.ini_options].
  • strict marker checking (--strict-markers).

Load pytest references when you need detailed rules.

When To Use

  • Bootstrapping tests for a new or existing Python repo.
  • Reorganizing tests that have become flat, slow, or difficult to extend.
  • Defining fixture boundaries before writing many assertions.
  • Creating only the first-layer scaffold for core behavior (not exhaustive coverage yet).

Inputs To Collect

  1. Target test scope: full repo, package, or module.
  2. Dependency profile: pure Python, DB, network/API, filesystem, UI/browser.
  3. Runtime expectation: what must be instant vs allowed to be slower.
  4. CI policy: which marker groups must block merges.

If these are missing, ask concise clarifying questions before editing.

Workflow

  1. Map source tree to test tree.
  2. Classify tests by dependency cost.
  3. Create minimal directories and placeholder test modules.
  4. Create fixture layers (tests/conftest.py plus local conftest.py in subtrees only when needed).
  5. Register markers and default selection behavior.
  6. Run collection and fast path tests.
  7. Report gaps and next extension points.

Step 1: Map Source To Tests

Create a mirrored structure rooted at tests/ that follows major source concepts.

Example mapping pattern:

  • src/app/services/job.py -> tests/app/services/test_job.py
  • src/app/ai/graphs/transcription.py -> tests/app/ai/graphs/test_transcription.py
  • src/app/api/routes.py -> tests/app/api/test_routes.py

Rules:

  • One initial test module per core source module.
  • Prefer test_<module>.py naming.
  • Keep directory mirrors shallow first; add deeper modules only where behavior is complex.

Step 2: Classify By Dependency Cost

Assign each test module to one initial class:

  • unit: no DB/network/filesystem side effects; instant execution.
  • integration: touches DB, HTTP stack, workflow runtime, or external services.
  • smoke: thin end-to-end confidence checks.

Decision logic:

  • If logic can run with fakes/stubs, make it unit.
  • If contract with framework/DB is essential, make it integration.
  • If validating a user-critical path across layers, make it smoke.

Step 3: Scaffold Minimal Test Modules

For each target module, scaffold:

  • import section
  • one happy-path test function
  • one error/edge test function
  • TODO comments indicating detail expansion points

Keep assertions minimal but behavior-focused. Avoid large fixtures in module files.

Step 4: Fixture Layering Strategy

Use fixture scopes based on cost:

  • function scope by default.
  • broader scopes (module/session) only for expensive setup with clear teardown.

Layer fixtures by directory:

  • tests/conftest.py: global, lightweight fixtures only (factories, deterministic defaults).
  • subtree conftest.py: domain-specific fixtures (API client, DB session, AI runtime stubs).

Guidelines:

  • Prefer yield fixtures for setup/teardown.
  • Keep fixtures atomic (one state-changing responsibility per fixture).
  • Avoid autouse except for truly universal behavior.

Step 5: Marker Taxonomy And Config

Ensure marker names are explicit and registered in pyproject.toml because strict markers are enabled.

Recommended baseline markers:

  • unit
  • integration
  • smoke
  • slow
  • external (requires network/service credentials)

Default run strategy:

  • Fast local path: run only unit by default in day-to-day iteration.
  • Full validation path: run all markers in CI or pre-release checks.

Step 6: Execution And Verification

Run commands in this order:

  1. uv run pytest --collect-only -q
  2. uv run pytest -m unit -q
  3. uv run pytest -q (if dependencies are available)

Optional targeted runs:

  • by node id for one test
  • by -k expression for focused iteration

Step 7: Completion Checks

A scaffold pass is complete when all are true:

  1. Every core source area has at least one corresponding test module.
  2. Unit tests run quickly and deterministically.
  3. Integration/external tests are isolated by marker and fixture boundaries.
  4. No unregistered marker warnings/errors.
  5. tests/ structure is understandable without extra documentation.
  6. A clear TODO path exists for deepening assertions later.

Branching Scenarios

  • If external APIs are required: provide stubs/mocks for unit tests; guard real calls behind external marker.
  • If DB is required: build a dedicated integration fixture layer and keep unit tests DB-free.
  • If tests become slow: split slow tests via marker and widen fixture scope only where safe.
  • If naming conflicts appear: keep unique test module names or package test directories explicitly.

Output Format

When applying this skill, provide:

  1. Proposed test tree diff.
  2. Marker and fixture plan.
  3. Exact commands for fast path and full path.
  4. Risks/open questions before writing detailed assertions.