Basic

Your first real test file

I kept asserts in a scratch file and ran the whole thing, and the first failure hid every claim after it. A test file with named functions lets the runner report each claim on its own line.

What you will see

test_add.py !. FAILED test_add.py::test_add_positive 1 failed, 1 passed

About 2 min of reading

Try It

Turn bare asserts into named test functions and let the runner report them.

1

Write a test file with two named claims about add.

def test_add_positive(): assert add(2, 3) == 5 def test_add_zero(): assert add(0, 0) == 0

Before you run it, say out loud what it will print. Then open the answer.

What it prints
>>>
2

Run the file with pytest. Both claims pass, so the summary says 2 passed.

pytest test_add.py

Before you run it, say out loud what it will print. Then open the answer.

What it prints
============================= test session starts ============================== collected 2 items test_add.py .. 2 passed ============================== 2 passed in 0.01s ===============================
3

Break one claim on purpose. The runner names the failing function and still runs the other.

pytest test_add.py

Before you run it, say out loud what it will print. Then open the answer.

What it prints
test_add.py !. FAILED test_add.py::test_add_positive 1 failed, 1 passed
The point of a runner over bare asserts: one failure does not hide the rest. You get a list of every broken promise, not just the first.

Put it in order

Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.

Data Flow

The runner finds every test_ function, calls each one, turns a raise into a failure, and prints a pass/fail summary.
1 Runner finds test_ functions
↓
2 Each function is called
↓
3 A raise becomes a failure
↓
4 Summary is printed
Why this works
One claim per function

The convention is one test function per promise. The function name is the sentence, and the body is the check. This is why the name matters: the runner prints it, so test_rejects_empty_input is a report a reader can understand without opening the file.

The runner is not magic

pytest collects the test_ functions, calls each, and catches the raise. A raise becomes a failure line, a clean return becomes a pass. You could write that loop yourself, the runner just saves you the bookkeeping and gives you the summary line.

Examples

Name the claim

def test_add_positive():
    assert add(2, 3) == 5

A function whose name starts with test_ is one claim. The name is what the runner prints when it reports the result, so a good name is a readable report.

Several claims

def test_add_zero():
    assert add(0, 0) == 0

def test_add_negative():
    assert add(-1, -1) == -2

Each function is its own claim. One failing function does not stop the others from running, so you see every broken promise at once.

Run the file

pytest test_add.py

The runner finds every test_ function, runs it, and prints a summary like 2 passed. You did not write the loop, the runner did.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← A test is a checkable sentence
Previous Lesson
A test is a checkable sentence
Next Lesson
What to assert on
What to assert on →