Первые тесты на pytest: проверяем, что код работает
За полчаса вы установите pytest, напишете первый тест, запустите набор и научитесь читать вывод. К концу будет зелёный прогон и понимание, где искать ошибку, если он красный.

К концу этой инструкции у вас будет установленный pytest, файл с несколькими тестами и понимание, как читать вывод при падении. Займёт это минут тридцать, если Python уже стоит. Пишу под pytest 8.x и Python 3.11+ — на более ранних версиях команды те же, отличается только вывод в мелочах.
Что нужно до начала
Проверьте, что перечисленное на месте. Если чего-то нет, вы упрётесь в это не на первом шаге, а на третьем, и будете гадать, что не так.
- Python 3.11 или новее. Проверка:
python --versionдолжна показать номер версии, а не ошибку «command not found». - Терминал, открытый в папке проекта. Не в домашней папке, а именно там, где лежит ваш код.
- Право ставить пакеты. Если
pip installругается на права — используйте виртуальное окружение (шаг 1), а неsudo.
Шаг 1. Создайте виртуальное окружение и поставьте pytest
Виртуальное окружение изолирует зависимости проекта от системного Python. Без него пакеты одного проекта мешают другому.
# создаём окружение в папке .venv
python -m venv .venv
# активируем: Linux/macOS
source .venv/bin/activate
# активируем: Windows (PowerShell)
.venv\Scripts\Activate.ps1
# ставим pytest
pip install pytest
Проверка: команда pytest --version выведет строку вида pytest 8.3.2. Если видите номер версии — pytest на месте.
Шаг 2. Напишите функцию, которую будем проверять
Тестировать нечего, пока нет кода. Создайте файл calc.py с простой функцией — на ней разберём весь механизм.
# calc.py
def add(a, b):
"""Складывает два числа."""
return a + b
def divide(a, b):
"""Делит a на b. При делении на ноль бросает ValueError."""
if b == 0:
raise ValueError("Делить на ноль нельзя")
return a / b
Проверка: файл сохранён в той же папке, где вы запускаете терминал. Команда ls (или dir на Windows) должна показать calc.py.
Шаг 3. Напишите первый тест
pytest находит тесты по соглашению об именах: файлы test_*.py или *_test.py, а внутри — функции, начинающиеся с test_. Создайте файл test_calc.py.
# test_calc.py
from calc import add
def test_add_positive():
# проверяем: 2 + 3 должно давать 5
assert add(2, 3) == 5
def test_add_with_zero():
assert add(10, 0) == 10
def test_add_negative():
assert add(-4, -6) == -10
Вся проверка держится на assert: если выражение после него истинно — тест прошёл, если ложно — pytest покажет, что именно не сошлось. Никаких специальных методов вроде assertEqual здесь не нужно.
Шаг 4. Запустите тесты
Из папки проекта выполните:
pytest
При успехе вы увидите строку с точками и итог зелёным цветом:
test_calc.py ... [100%]
3 passed in 0.01s
Каждая точка — один прошедший тест. Проверка: три точки и 3 passed. Если хотите видеть имена тестов, а не точки, добавьте флаг -v:
pytest -v
Зелёный прогон не значит, что код правильный. Он значит, что код ведёт себя так, как вы описали в тестах. Тесты проверяют ваши предположения, а не истину.
Шаг 5. Проверьте, что тест умеет падать
Тест, который никогда не падает, бесполезен. Временно сломайте проверку, чтобы увидеть красный вывод и научиться его читать. Поменяйте в первом тесте == 5 на == 6 и запустите снова:
def test_add_positive():
assert add(2, 3) == 6 # намеренная ошибка
def test_add_positive():
> assert add(2, 3) == 6
E assert 5 == 6
E + where 5 = add(2, 3)
test_calc.py:5: AssertionError
1 failed, 2 passed in 0.02s
pytest показывает, что add(2, 3) вернул 5, а тест ждал 6. Строка + where 5 = add(2, 3) — это интроспекция assert: pytest сам подставляет фактические значения. Верните == 5 обратно.
Шаг 6. Проверьте исключение
Код должен не только возвращать значения, но и падать там, где нужно. Проверка, что функция бросает исключение, делается через pytest.raises — это контекстный менеджер, внутри которого ожидается ошибка.
# добавьте в test_calc.py
import pytest
from calc import divide
def test_divide_ok():
assert divide(10, 2) == 5
def test_divide_by_zero_raises():
# ждём, что деление на ноль бросит ValueError
with pytest.raises(ValueError):
divide(10, 0)
Если внутри блока with pytest.raises(ValueError) исключение не возникнет — тест упадёт. Это ловит частую ошибку: забыли проверку на ноль, функция вернула что-то странное вместо ошибки.
Шаг 7. Один тест на много входных данных
Три почти одинаковых теста test_add_* — это повторение. @pytest.mark.parametrize прогоняет одну функцию с разными наборами данных. Каждый набор считается отдельным тестом в отчёте.
import pytest
from calc import add
@pytest.mark.parametrize("a, b, expected", [
(2, 3, 5),
(10, 0, 10),
(-4, -6, -10),
(0, 0, 0),
])
def test_add(a, b, expected):
assert add(a, b) == expected
Проверка: pytest -v покажет четыре строки с разными параметрами в квадратных скобках, например test_add[2-3-5] PASSED. Один упавший набор не мешает остальным отработать.
Если не получилось
Пять ошибок, на которые новичок натыкается в первый час.
ModuleNotFoundError: No module named 'calc'— вы запускаете pytest не из той папки, где лежитcalc.py, либо файлы в разных директориях. Перейдите в папку проекта и запуститеpytestоттуда.collected 0 items— pytest не нашёл ни одного теста. Проверьте имена: файл должен начинаться сtest_, функции внутри — тоже сtest_. Файлcalc_tests.pyили функцияcheck_addсобраны не будут.pytest: command not found— окружение не активировано или pytest не установлен. Повторите шаг 1, убедитесь, что в начале строки терминала появилось(.venv).SyntaxErrorпри сборке — pytest импортирует ваши файлы, и опечатка вcalc.pyломает сбор ещё до запуска тестов. Прочитайте номер строки в ошибке и исправьте сам код, а не тест.- Тест зелёный, но код явно неверный — скорее всего в
assertвы сравниваете функцию с самой собой или условие всегда истинно. Специально сломайте проверку (шаг 5): если тест остаётся зелёным при неверном ожидаемом значении — он ничего не проверяет.
Что дальше
Когда первый набор зелёный, двигайтесь по трём направлениям. Разбейте тесты по папке tests/, чтобы они не мешались с кодом. Посмотрите на фикстуры (@pytest.fixture) — они готовят данные и объекты, которые нужны нескольким тестам, чтобы не дублировать подготовку. И подключите измерение покрытия через плагин pytest-cov (pip install pytest-cov, запуск pytest --cov) — покрытие покажет, какие строки кода тесты вообще не трогают. Точный набор флагов и опции сверяйте с официальной документацией pytest для вашей версии.
Тест и проверочная: Быстрые ответы с объяснением, почему остальные варианты неверны
Спросить за 10 ₽Источники: pytest — официальная документация, pytest: How to write and report assertions in tests
Частые вопросы
У меня pytest 7, а не 8. Всё сломается?
Тест зелёный, но я не уверен, что он что-то проверяет. Как убедиться?
Нужно ли класть тесты в отдельную папку?
Чем assert в pytest отличается от обычного assert в Python?
Почему pytest пишет «collected 0 items»?
Как запустить только один тест, а не весь набор?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.