Testing with pytest
Write and run real Python tests with the de-facto standard tool.
- Write tests as plain functions with assert
- Run a suite and read its output
- Use fixtures and parametrization to reduce repetition
The testing fundamentals lesson covered why and what to test. pytest is how
it's done in Python — the de-facto standard, loved for letting you write tests as
ordinary functions with plain assert.
A test is just a function
Put functions starting with test_ in a file starting with test_, and assert
what you expect (arrange–act–assert from the testing lesson):
# test_math.py
from mymath import sum_to
def test_sums_small():
assert sum_to(3) == 6 # arrange + act + assert in one line
def test_zero():
assert sum_to(0) == 0Run the whole suite by just typing pytest. It discovers your tests, runs them,
and on failure shows the exact values that didn't match — no boilerplate
assertion methods to memorise.
Testing that errors are raised
Confirm code fails when it should, using pytest.raises:
import pytest
def test_rejects_negative():
with pytest.raises(ValueError):
set_age(-1)Fixtures and parametrization
Two features remove most test repetition:
- Fixtures provide reusable setup (a temp file, a database connection, sample data) to any test that asks for it by name — clean arrange steps, shared across tests.
- Parametrization runs one test over many inputs:
@pytest.mark.parametrize("n, expected", [(1, 1), (3, 6), (5, 15)])
def test_sum_to(n, expected):
assert sum_to(n) == expectedThat's three cases (the boundary-hunting habit from the testing lesson) in a few lines, each reported separately.
Wire pytest into the CI pipeline (the build-and-release lesson) so the suite
runs on every push. Tests that only run when someone remembers are tests that
quietly rot.
Where to go next
That completes the Python Advanced tier. The JavaScript/TS track has its own Advanced tier covering the runtime's internals, builds, and advanced types.