UpPetto

This is a real course generated by UpPetto — unedited.

AI-generated. Two models wrote it, a third checked every risky claim against sources, a fourth re-checked.

Create my own course

pytest: тестирование без ручного assert-фреймворка

core написать и запустить набор тестов pytest для функции, включая простую фикстуру или параметризацию

В C дисциплину тестирования вы получали через фреймворки типа CUnit/Check/Unity: писали main(), который регистрирует тесты, вручную формулировали макросы вида ASSERT_EQUAL, сами настраивали компиляцию тестового бинарника отдельно от основного. Это работающая, но тяжёлая инфраструктура — почти столько же кода на обвязку, сколько на сами проверки.

pytest убирает этот слой почти полностью. Тест — это обычная функция с именем test_*, обычный assert, без специальных макросов и без ручной регистрации. pytest перехватывает AssertionError от встроенного assert и сам генерирует подробный отчёт — что было слева, что справа, — не требуя от вас assert_equal(a, b) с раздельными аргументами, как в C-фреймворках.

def add(a, b):
    return a + b

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

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

Запуск: pytest в корне проекта находит все test_*.py и все функции test_* внутри без единой строки регистрации — сравните это с ручным заполнением таблицы указателей на тестовые функции в C.

Повторяющийся setup/teardown из C-тестов (открыть файл/соединение перед тестом, закрыть после — часто вручную дублируемый в каждом тесте) в pytest заменяется фикстурами (fixtures): функцией с декоратором @pytest.fixture, которую тест запрашивает как параметр. Фикстура может делать yield, разделяя код на «до» и «после» — тот же паттерн, что и with для контекстных менеджеров, только применённый к тестовой инфраструктуре.

import pytest

@pytest.fixture
def tmp_config():
    cfg = {"debug": True}
    yield cfg           # тест получит именно это значение
    cfg.clear()         # выполнится после теста

def test_config_debug(tmp_config):
    assert tmp_config["debug"] is True

Второй паттерн, который в C приходится писать циклом с ручным перебором тестовых кейсов внутри одного main, — это параметризация. @pytest.mark.parametrize превращает одну функцию в набор независимых тестов, каждый со своим отчётом об успехе/провале, без общего цикла, где один упавший assert останавливает проверку остальных случаев.

@pytest.mark.parametrize("a, b, expected", [
    (2, 3, 5),
    (-1, -1, -2),
    (0, 0, 0),
])
def test_add_parametrized(a, b, expected):
    assert add(a, b) == expected

Частая ошибка C-программиста здесь — тащить привычку «один main, который последовательно печатает PASS/FAIL и возвращает код ошибки процесса». В Python вам не нужен ни main, ни ручной подсчёт: pytest сам агрегирует результаты, сам определяет код возврата процесса, и вы не пишете инфраструктуру — только содержательные проверки.

Разобранный пример

Рассмотрим функцию деления с проверкой на ноль:

def safe_divide(a, b):
    if b == 0:
        raise ValueError("division by zero")
    return a / b

C-стиль теста был бы: вызвать функцию, вручную проверить код возврата и напечатать сообщение об ошибке. В pytest для проверки исключения используется pytest.raises как контекстный менеджер — не код ошибки, а сам факт исключения — потому что в Python исключение и есть штатный сигнал об ошибке (разобрано в модуле про исключения):

import pytest

def test_safe_divide_by_zero():
    with pytest.raises(ValueError):
        safe_divide(1, 0)

def test_safe_divide_normal():
    assert safe_divide(10, 2) == 5

Никакой ручной проверки «если исключение не было выброшено — тест должен упасть» писать не нужно: pytest.raises сама провалит тест, если блок отработал без исключения.

Попробуй сейчас

Напишите функцию is_palindrome(s: str) -> bool. Покройте её тестами через @pytest.mark.parametrize минимум пятью случаями (пустая строка, один символ, палиндром, не палиндром, палиндром с разным регистром — решите сами, нормализовать ли регистр внутри функции). Запустите pytest -v и убедитесь, что вывод показывает каждый параметризованный случай отдельной строкой.

Ожидаемый результат: 5+ отдельных PASS/FAIL в выводе, без единого написанного вами if/print для диагностики.

Получилось, если…

Вы поняли идею, если для новой функции первым инстинктом стало написать test_*.py с обычными assert, а не собственный мини-фреймворк проверки с кодами возврата, и вы бы использовали parametrize вместо for-цикла с несколькими assert подряд в одном тесте.

Вывод

pytest — это assert без макросов, setup/teardown без дублирования (фикстуры) и таблица тестовых случаев без ручного цикла (параметризация). Вся инфраструктура регистрации и отчётности, которую в C пишете сами, здесь уже есть.

AI-generated · source-grounded review

🛡 Fact-checked: 2 risky claims verified · 2 removed · confidence: high
[verified] pytest — фреймворк тестирования Python, тесты — функции test_* с обычным assert
KB называет pytest фреймворком тестирования в профессиональной экосистеме
[softened] pytest формирует дифф через интроспекцию AST
внутренний механизм отсутствует в KB; оставлено «генерирует подробный отчёт»
[softened] код после yield в фикстуре выполняется, даже если тест упал
гарантия не описана в KB; убрано уточнение про падение
[verified] Исключения — штатный механизм сигнализации об ошибке, поэтому проверяются через pytest.raises, а не через код возврата
KB: исключения — основной механизм ошибок в Python, в отличие от кодов возврата в C
[removed] Утверждение о механизме генерации диффа «используя интроспекцию AST» ослаблено до «генерирует подробный отчёт» — конкретная внутренняя реализация pytest в KB не описана
[removed] «выполнится после теста, даже если он упал» ослаблено до «выполнится после теста» — гарантии выполнения кода после yield при падении в KB не описаны

A second model re-checked this lesson's claims and corroborated 2 of 4 , and could not confirm 2 either way. This is incomplete corroboration, not a disagreement.

Key concepts: pytest assert фикстуры
Tell me more 🔒 Didn't understand — explain simply 🔒 Show examples 🔒 Sources 🔒

On your own course these buttons answer instantly, quizzes track what you've mastered, and lessons adapt to your gaps. Write my course

Check yourself

1. Как pytest определяет провал теста?
2. Для чего в первую очередь используются фикстуры pytest?
3. Что делает @pytest.mark.parametrize по сравнению с ручным циклом for с несколькими assert в одном тесте?
On your own course, these are marked as you answer