@pytest.fixture decorator, pass fixture name as test parameter
Temporary folder
Built-in tmp_path fixture (cleaned up automatically)
Debugging Strategy Ladder
Use these in order — start with the simplest and escalate if needed:
Read the error message — Python traceback tells you the file, line, and exception type
Print debugging — add print() calls to see intermediate values
Rubber duck debugging — explain the code line by line out loud
Binary search debugging — check the midpoint, narrow to the half that's broken
VS Code debugger — set breakpoints, step through, inspect variables interactively
Writing Testable Code
Hard to Test
Easy to Test
Calls input() and print()
Takes parameters, returns values
Reads from hardcoded file paths
Accepts file path as parameter
Depends on global state
Pure function — no side effects
Does I/O and logic in one function
Logic separated from I/O
Key Distinctions
What Beginners Think
What Professionals Know
Testing is extra work
Testing saves time in the long run
100% coverage = fully tested
Coverage measures breadth, not depth
"It works on my machine"
If there's no test, you don't actually know it works
Write code first, test later
TDD: define "working" first, then code
Tests are for big projects
Even small scripts benefit from a few key tests
Common Edge Cases Checklist
Always test these for any function that accepts a collection or number:
- Empty collection ([], "", {})
- Single element ([42], "x")
- All identical values ([5, 5, 5])
- Zero (0, 0.0)
- Negative values (-1, -100)
- Boundary values (exactly at the threshold where behavior changes)
- Very large values
What's Next
Chapter 14: Object-Oriented Programming — you'll bundle data and behavior into classes. TDD will help you design clean class interfaces from day one.