fix
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
# Diagnose Test Failures — Escalation Ladder
|
||||
|
||||
When the default `--tb=short -q` output is not enough to identify a root cause,
|
||||
escalate through these levels in order. Stop as soon as the cause is clear.
|
||||
|
||||
## Level 1 — Full Traceback
|
||||
|
||||
Use when: short traceback cuts off the relevant frame.
|
||||
|
||||
```bash
|
||||
pytest {failing_tests} --tb=long -v
|
||||
```
|
||||
|
||||
What to look for:
|
||||
- The exact line that raises, with full local variable context
|
||||
- Which fixture or conftest call is involved
|
||||
- Whether the error originates in test code or production code
|
||||
|
||||
---
|
||||
|
||||
## Level 2 — Show Local Variables
|
||||
|
||||
Use when: you see the line but not the values involved.
|
||||
|
||||
```bash
|
||||
pytest {failing_tests} --tb=long -v --showlocals
|
||||
```
|
||||
|
||||
What to look for:
|
||||
- Values of `self`, `result`, `response`, `exc` at the point of failure
|
||||
- Unexpected `None` where an object was expected
|
||||
- Wrong type (e.g. `str` instead of `int`)
|
||||
|
||||
---
|
||||
|
||||
## Level 3 — Verbose + Full Diff
|
||||
|
||||
Use when: `AssertionError` with truncated diff output.
|
||||
|
||||
```bash
|
||||
pytest {failing_tests} -vv --tb=long --showlocals
|
||||
```
|
||||
|
||||
`-vv` forces pytest to print the full comparison diff without truncation.
|
||||
|
||||
What to look for:
|
||||
- Exact difference between actual and expected dicts/lists/objects
|
||||
- Whitespace or encoding differences in string comparisons
|
||||
- Extra or missing keys in dicts
|
||||
|
||||
---
|
||||
|
||||
## Level 4 — Capture Disabled (see print/log output)
|
||||
|
||||
Use when: test uses `print()` or `logging` for debug output, but it's suppressed.
|
||||
|
||||
```bash
|
||||
pytest {failing_tests} -s --tb=long -v
|
||||
```
|
||||
|
||||
`-s` disables stdout/stderr capture — all print and logging output becomes visible.
|
||||
|
||||
What to look for:
|
||||
- Debug prints left in test or production code
|
||||
- Log messages from libraries (SQLAlchemy queries, httpx requests)
|
||||
- Side effects happening before the assertion
|
||||
|
||||
---
|
||||
|
||||
## Level 5 — Log Level Override
|
||||
|
||||
Use when: need structured log output, not just prints.
|
||||
|
||||
```bash
|
||||
pytest {failing_tests} --log-cli-level=DEBUG --tb=long -v
|
||||
```
|
||||
|
||||
What to look for:
|
||||
- DB query output (sqlalchemy echo)
|
||||
- HTTP request/response details
|
||||
- Background task or worker lifecycle events
|
||||
|
||||
---
|
||||
|
||||
## Level 6 — Single Test Isolation
|
||||
|
||||
Use when: a test passes alone but fails in suite (ordering issue or shared state).
|
||||
|
||||
```bash
|
||||
# Run the failing test in complete isolation
|
||||
pytest {single_failing_test_id} --tb=long -v --forked
|
||||
# or without pytest-forked:
|
||||
pytest {single_failing_test_id} --tb=long -v -p no:randomly
|
||||
```
|
||||
|
||||
Also try running with `--randomly-seed=0` to fix the order and reproduce reliably.
|
||||
|
||||
What to look for:
|
||||
- Global state mutated by a previous test
|
||||
- Singleton or module-level cache not reset between tests
|
||||
- `autouse` fixture with session scope polluting state
|
||||
|
||||
---
|
||||
|
||||
## Level 7 — Collection Errors
|
||||
|
||||
Use when: pytest crashes before running any test (`ERROR collecting ...`).
|
||||
|
||||
```bash
|
||||
pytest {target} --collect-only --tb=long
|
||||
```
|
||||
|
||||
What to look for:
|
||||
- `SyntaxError` in test file
|
||||
- Import-time crash (`ModuleNotFoundError`, circular import)
|
||||
- Missing fixture at discovery time
|
||||
- Plugin conflict (`conftest.py` error)
|
||||
|
||||
---
|
||||
|
||||
## Common Root Causes Quick Reference
|
||||
|
||||
| Symptom | Likely Cause | Fix Hint |
|
||||
|---------|-------------|----------|
|
||||
| `fixture 'X' not found` | Fixture not in scope or conftest not loaded | Check conftest path; add `conftest.py` to package |
|
||||
| `coroutine was never awaited` | `async def` called without `await` in sync context | Add `@pytest.mark.asyncio` or set `asyncio_mode=auto` |
|
||||
| `TypeError: __init__() missing argument` | Production code signature changed | Update fixture to pass new required arg |
|
||||
| `AssertionError` with None | Mock not set up / fixture returns None | Check mock `return_value`; check fixture creates object |
|
||||
| `ImportError` on test file | Wrong `PYTHONPATH` or missing `__init__.py` | Check `pythonpath` in `[tool.pytest.ini_options]` |
|
||||
| `RuntimeError: Event loop closed` | Async fixture scope mismatch | Match fixture scope to `asyncio_mode`; use `loop_scope` |
|
||||
| All tests suddenly fail | Broken conftest or missing env var | Run `--collect-only` first; check `.env.test` |
|
||||
Reference in New Issue
Block a user