unittest против pytest
Прямое сравнение unittest и pytest и обзор экосистемы pytest.
Экосистема pytest — большой набор плагинов (для покрытия, моков, асинхронности, повторов), которые подключаются одной установкой.
Сводная таблица сравнения
| Критерий | unittest | pytest |
| Установка | встроен в Python | pip install pytest |
| Структура теста | класс + методы | функции (можно и классы) |
| Проверки | методы assert* | обычный assert |
| Фикстуры | setUp/tearDown | @pytest.fixture по аргументам |
| Параметризация | subTest | @pytest.mark.parametrize |
| Плагины | мало | огромная экосистема |
| Многословность | выше | ниже |
Когда что выбирать
Это не «или-или», а вопрос контекста:
- unittest — когда нельзя ставить зависимости (минимальное окружение, скрипт, стандартная поставка), или когда тестов немного. Он всегда «уже есть».
- pytest — для большинства реальных проектов: меньше шаблонного кода, удобные фикстуры и параметризация, богатая экосистема.
Важно: оба инструмента опираются на одни и те же идеи — TestCase/AAA, фикстуры, параметризация, моки. Освоив unittest, вы перейдёте на pytest за вечер: меняется синтаксис, а не принципы.
Мокинг в обоих мирах
Хорошая новость: unittest.mock (Mock, patch, MagicMock) — это часть стандартной библиотеки и одинаково работает и в unittest, и в pytest. То, что вы изучили в разделе про моки, переносится без изменений. Для pytest есть и обёртка-плагин pytest-mock (фикстура mocker), но под капотом тот же unittest.mock.
# pytest-стиль с фикстурой mocker (плагин pytest-mock)
def test_roll(mocker):
mocker.patch("random.randint", return_value=4)
assert lucky_roll() == 4
Популярные плагины pytest
| Плагин | Что добавляет |
pytest-cov | отчёт о покрытии кода тестами |
pytest-mock | удобная фикстура mocker поверх unittest.mock |
pytest-xdist | параллельный запуск тестов на нескольких ядрах |
pytest-asyncio | тестирование async-кода |
pytest-django | интеграция с Django |
Подключаются плагины просто установкой — pytest сам их подхватывает:
pip install pytest-cov pytest-mock
# запуск с измерением покрытия:
pytest --cov=myapp
Миграция с unittest на pytest
Переход обычно делают постепенно, без переписывания всего разом. Сначала ставят pytest и просто запускают им существующие unittest-тесты — команда меняется с python -m unittest на pytest, а сами тесты остаются как есть. Затем новые тесты пишут уже в pytest-стиле (функции вместо классов, assert вместо assertEqual), а старые трогают только при изменении соответствующего кода. Так проект живёт со смешанным набором тестов, и это нормально — оба стиля прекрасно сосуществуют в одном прогоне.
Один нюанс: фикстуры setUp/tearDown работают только внутри классов-наследников TestCase. Если переписываете тест в pytest-функцию, замените их на фикстуры @pytest.fixture. А вот моки переносятся буквально без изменений — это всё тот же unittest.mock.
Главный вывод раздела
Не нужно выбирать «навсегда». Начните с unittest — он встроен и учит фундаменту: TestCase, фикстуры, assert-методы, моки. Когда проект вырастет, переходите на pytest ради краткости, удобных фикстур и богатой экосистемы плагинов; ваши прежние тесты при этом продолжат запускаться. Принципы тестирования — изоляция, детерминизм, осмысленные проверки — важнее конкретного фреймворка и одинаково применимы к обоим.
Итог
- unittest встроен и хорош для минимального окружения; pytest удобнее для крупных проектов.
- Идеи (AAA, фикстуры, параметризация, моки) общие — переход между ними лёгкий.
unittest.mockработает в обоих фреймворках одинаково.- Экосистема плагинов pytest (
cov,mock,xdist) подключается установкой.