Implementations seeded with exactly one deliberate defect, and every lesson actually runs them to see which technique catches which bug and which one it misses. Equivalence partitioning, boundary values, decision tables, state transitions, pairwise and MC/DC. Twenty lessons that turn "checking by instinct" into "being able to explain why it fails".
The 20 lessons are grouped into five chapters. Working through them in order is the best way, but you are welcome to read only the technique you need right now. Note: every result shown was produced by actually running Python 3.14 with pytest 9. Install pytest if you want to follow along.
You first see in numbers why "try everything" never finishes, then decide where each test belongs using test levels and types, and settle on the shape of a readable test case. This becomes the shared vocabulary for the next seventeen lessons.
Starting with equivalence partitioning, which folds 100,000 cases into 22 groups, then boundary values, decision tables, state transitions and pairwise. Each lesson drops one bug the previous technique could not find.
Organising combinations of conditions with CFD and cause-effect graphs, building flow-shaped tests from use cases, and finally exploratory testing. What surfaces here is less often a bug than something nobody decided.
Statement, branch and condition coverage, then MC/DC. What coverage actually counts, and why bugs survive at 100%. The meaning of the number and its limits, together.
Telling the test doubles apart, where fixtures belong, how to read a performance test, how to write a report that gets fixed, and prioritising by risk. It closes by folding all twenty lessons onto one page.
Once you have finished all 20 lessons, firm up the language you write tests in with Python for Beginners, or take it to the team level with Agile and Scrum. Membership unlocks every course.