pytest: Writing and running tests in Python
In short: pytest is the most popular test framework for Python: tests are simple functions with assert, fixtures supply test data, and error messages are informative.
In more detail: With pytest you write tests without classes and without boilerplate. It finds files test_*.py and functions test_* automatically. Installation: pip install pytest.
In Depth
A simple test
# calc.py
def add(a, b):
return a + b
# test_calc.py
from calc import add
def test_add_positive_numbers():
assert add(2, 3) == 5
def test_add_negative_numbers():
assert add(-1, -1) == -2Start with pytest or pytest -v (verbose), pytest -k add (filter by name), pytest -x (stop at first failure). On failure pytest shows the compared values precisely.
Parametrised tests and exceptions
import pytest
@pytest.mark.parametrize("a, b, expected", [(1, 1, 2), (0, 0, 0), (-1, 1, 0)])
def test_add(a, b, expected):
assert add(a, b) == expected
def test_division_by_zero():
with pytest.raises(ZeroDivisionError):
1 / 0One test, many data sets: saves code and covers edge cases.
Fixtures
@pytest.fixture
def user():
return {"name": "Mia", "age": 17}
def test_name(user):
assert user["name"] == "Mia"Fixtures provide reusable test preparation (database, temporary files with tmp_path, test clients) and clean up afterwards with yield. Shared fixtures live in conftest.py. With monkeypatch and unittest.mock you replace external dependencies.
Practice
- Structure:
tests/folder, tests independent of each other, fast unit tests first. - Measure coverage:
pytest --cov=package(plugin pytest-cov). - Markers (
@pytest.mark.slow) separate slow tests. - CI: run pytest in GitHub Actions on every push.
- A test should check one thing and have an expressive name. TDD (test first) helps with design.
Alternatives:
unittest(in the standard library),nose(deprecated),hypothesisfor property-based testing.
See also: Unit test, Mocking and test doubles, Code coverage, Property-based testing