diff --git a/docs/guide_testing.md b/docs/guide_testing.md index 0f521d48..8b22f0f2 100644 --- a/docs/guide_testing.md +++ b/docs/guide_testing.md @@ -652,4 +652,33 @@ See `src/gui_2.py:601-606` for the canonical implementation. This pattern unbloc - **`src/api_hook_client.py`** — The Python wrapper for the Hook API used in integration tests - **`tests/conftest.py`** — The canonical source of all fixtures documented in this guide +--- + +## Pattern: Narrow Test Paths vs. Kitchen-Sink Functions + +**Anti-pattern: calling a kitchen-sink function.** A test that does `gui_2.render_main_interface(app_instance)` requires mocking 50+ imgui/imscope methods because `render_main_interface` dispatches to dozens of nested render functions. Adding a single mock for `imscope.window` (to return a tuple) just reveals the next un-mocked dependency (e.g. `imgui.begin` returning bool where a 2-tuple is expected). The test never reaches its assertion. + +**Better pattern: test the narrow function.** Most render flows have a dedicated sub-function (e.g. `render_prior_session_view`, `render_preset_manager_window`, `render_theme_panel`). Refactor the test to call the narrow function directly with mocks scoped to what *that* function actually uses. Example outcome: + +- `render_main_interface` test: 50+ mocks, ~6s runtime, flakiness on every un-mocked imgui call. +- `render_prior_session_view` test: 20 mocks, ~0.08s runtime, stable. + +**When to refactor vs. add mocks:** +- If the test intent is "verify push/pop balance in the prior-session render path", call the narrow function. +- If the test intent is "verify the whole GUI render path is correct", accept the 50+ mock cost (and ensure all mocks are correct). + +See the `prior_session_test_harden_20260605` plan in `docs/superpowers/plans/` for the concrete refactor example. + +--- + +## Pattern: Indentation-Driven Method Visibility + +**The bug:** A class method defined with the right intent (2-space indent) may be parsed as nested inside a previous function if indentation is off by even one space. The file "passes" syntactically (imports OK) but the method is **not** on the class — `hasattr(App, 'method_name')` returns `False`. Any production code that calls `app.method_name` falls through to `__getattr__`, which delegates to the controller (which also doesn't have the method), and a cryptic `AttributeError` is raised at runtime. + +**How to detect:** +- Use AST to list all App methods: `uv run python -c "import ast; tree = ast.parse(open('src/gui_2.py').read()); [print(item.name) for n in ast.walk(tree) if isinstance(n, ast.ClassDef) and n.name == 'App' for item in n.body if isinstance(item, ast.FunctionDef)]"`. +- The skeleton via `manual-slop_py_get_skeleton` should show the method as a class member. + +**How to fix:** Re-indent the affected method to 2-space class level. Run the failing test to confirm. See the `live_gui_test_hardening_v2_20260605` track in `conductor/tracks.md` for the concrete example (where `_capture_workspace_profile` was being parsed as nested inside `_apply_snapshot` due to a 1-space indentation drift after a cleanup commit). + See [guide_architecture.md](guide_architecture.md) for the overall architecture and [conductor/workflow.md](../../conductor/workflow.md) for the TDD protocol that the test suite implements.