One test
A unit is the smallest piece of code you can check on its own, usually one function. Here it is add, in the file calc.py. Give it two numbers, a and b, and it returns their sum.
A unit test is a small function that calls the unit with known input. test_add calls add(2, 3). We picked numbers whose right answer we already know: 5.
add returns 5. The assert line checks that add(2, 3) == 5 is true. True: the test passes, green. False: Python raises an AssertionError and the test fails, red.
Bugs like to hide at the edges: zero, negative numbers, empty input, very big values. test_edges checks add(-2, 2) == 0, a negative number and an answer of zero. It passes too.
Run them often
Type pytest in a terminal. It finds files named test_*.py, runs every function whose name starts with test, and prints a summary: 2 passed. It takes well under a second, so run it often.
Someone edits add and slips: return a + a. Run the tests after every change. add(2, 3) now gives 4, not 5, so pytest prints 2 failed. The mistake is caught in seconds.
Put back a + b and run pytest again: 2 passed. Tests are a safety net. You can change code without fear, because a test that knows the right answer will complain when something breaks.
Test first
Test-driven development flips the order. Red: first write a test for the next feature, add(1, 2, 3) == 6, and watch it fail. add only takes two numbers, so Python raises a TypeError.
Green: write just enough code to pass. Give add a third number, c, that is 0 when you leave it out. Run pytest: 3 passed. The old tests still pass, so nothing else broke.
Refactor: tidy the code. *nums takes any number of values and sum adds them, so add now also accepts four or more numbers. Run pytest: still 3 passed, so the old cases still work. Then repeat the cycle.
More uses
Tests are also documentation. Read them and you learn what add does: add(2, 3) is 5, add(-2, 2) is 0, add(1, 2, 3) is 6. Unlike a comment, they are checked on every run.
Some units call something slow or outside, like a web service or the clock. A test can swap that part for a mock: a stand-in that gives a fixed answer. Python has unittest.mock for this.
What happens
;; step 1/12 · unit
# calc.py
def add(a, b):
return a + b
unit one small piece of code: here, one function
input two numbers, a and b
output what it returns: a + b
test_calc.py also starts with the line from calc import add, so the tests can call add. The card leaves it out to save room. Every result in this figure was checked by running these exact files with pytest 9.1.1 on Python 3.14. The terminal is trimmed to fit: real pytest prints the file name before the dots (test_calc.py ..) and how long the run took.