Test Fixture Pattern — Module-Scoped Immutable Template + Per-Test Copy

A reusable performance pattern distilled from the 2026-08-05 tests/tools/ test_search.py surgery in orchestrata-ultimate.

The lesson (measured)

The suite's rule is function-scope fixtures only (no module/class scope for mutable state). Applied blindly, a function-scoped real_registry fixture rebuilt build_tool_registry() for every test — and ToolSearchTool.create() runs a cache-warm fuzzy sweep (~0.6–0.9 s of difflib.SequenceMatcher) each time. Paid ~128×/module, that pure rebuild cost dominated the module:

The pattern

  1. Build the expensive object ONCE in a module-scoped fixture, treating it as an immutable template — nothing may mutate it after construction.
  2. Each test receives a cheap per-test copy made through the public API (a constructor/factory that reuses the template's expensive precomputed core — never deepcopy of internals, never handing tests the shared instance itself when they mutate it).
  3. Mark the module-scoped fixture with the sanctioned # T4-immutable-module-scope: <reason> marker — the explicit, reviewed exemption token for the function-scope-only rule (T4), so the test reviewer passes it deliberately rather than by loophole.

When it applies

When function scope must stay

Pointers (orchestrata-ultimate)

tests/tools/test_search.py (the applied example) · suite standards: AGENTS.md § Test Standards, scripts/test-review.py (T-rules) · batch impact: per-module durations in .cache/test_durations.json drive LPT packing in scripts/run_tests.py.