Гид по технологиям

Тестирование с Pytest: руководство по настройке и лучшим практикам

5 min read Тестирование Обновлено 03 Jan 2026
Pytest: настройка, примеры и лучшие практики
Pytest: настройка, примеры и лучшие практики

Фрагмент кода на экране компьютера

Зачем нужен Pytest

Тестирование помогает ловить ошибки на ранних стадиях разработки и снижает риски при изменениях кода. Pytest популярен из‑за простого синтаксиса, богатой экосистемы плагинов и гибкой системы фикстур.

Краткое определение: Pytest — фреймворк для тестирования Python, ориентированный на читабельность тестов и их расширяемость.

Important: этот материал практичен — примеры можно копировать и запускать локально.

Установка и изоляция окружения

Перед установкой рекомендуем создать виртуальное окружение, чтобы зависимости тестов не смешивались с глобальными пакетами.

Создайте виртуальное окружение командой:

python -m venv tests

Активируйте окружение на Linux/macOS:

source tests/bin/activate

На Windows используйте команду (точно с двумя обратными слэшами в пути):

tests\\Scripts\\activate

Установите Pytest через pip:

pip install pytest

Проверьте установку:

pytest --version

Если команда вернула версию, Pytest установлен и готов к использованию.

Первый тест: простая функция сложения

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

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

Что может пойти не так: входные данные могут быть не числовыми (None, строка и т. п.), что приведет к исключению TypeError.

Напишите первый тест, чтобы проверить ожидаемое поведение:

def test_add_numbers():
    assert add_numbers(2, 3) == 5
    assert add_numbers(-1, 1) == 0
    assert add_numbers(0, 0) == 0

Этот тест проверяет базовые сценарии. Каждый assert сравнивает фактический результат с ожидаемым.

Как запускать тесты

Перейдите в папку с тестами и выполните:

pytest

Pytest автоматически найдет файлы, начинающиеся с test_ или заканчивающиеся на _test.py, и запустит тесты.

Если один из assert не совпал с ожидаемым значением, Pytest выведет удобный отчет с входными данными и фактическим результатом.

Вывод pytest с информацией об одном неуспешном тесте

Изображение показывает типичный вывод: какой assert не прошёл и какие были входные значения.

Проверка исключений с помощью pytest.raises

Чтобы тестировать сценарии, в которых функция должна поднять исключение, используйте контекстный менеджер pytest.raises.

import pytest

def test_add_numbers_with_invalid_inputs():
    with pytest.raises(TypeError):
        add_numbers(None, 2)

Если исключение не будет поднято, тест провалится. Чтобы проверить текст исключения, сохраните контекст в переменную и сравните сообщение:

def test_add_numbers_with_invalid_inputs():
    with pytest.raises(TypeError) as exc_info:
        add_numbers(None, 2)

    assert exc_info.value.args[0] == 'unsupported operand type(s) for +: \'NoneType\' and \'int\''

Обратите внимание: строки сравниваются буквально, поэтому тест чувствителен к формулировке исключения.

Параметризованные тесты

Параметризация позволяет запускать одну функцию теста с набором входных данных. Это делает тесты короче и чище:

import pytest

@pytest.mark.parametrize('a,b,expected', [
    (2, 3, 5),
    (-1, 1, 0),
    (0, 0, 0)
])
def test_add_numbers(a, b, expected):
    assert add_numbers(a, b) == expected

Pytest выполнит три итерации этого теста с разными наборами значений.

Группировка тестов в классах

Для организации большого количества тестов удобно использовать тестовые классы. Класс должен начинаться с Test, чтобы Pytest распознал его:

class TestAddFunction:
    @pytest.mark.parametrize('a, b, expected', [
        (2, 3, 5),
        (-1, 1, 0),
        (0, 0, 0),
    ])
    def test_addition_with_numbers(self, a, b, expected):
        assert add_numbers(a, b) == expected

    def test_add_numbers_with_invalid_inputs(self):
        with pytest.raises(TypeError) as exc_info:
            add_numbers(None, 2)
        assert exc_info.value.args[0] == 'unsupported operand type(s) for +: \'NoneType\' and \'int\''

Заметьте: методы класса получают параметр self, но Pytest не использует unittest-инфраструктуру, поэтому можно писать простые тестовые классы без boilerplate.

Фикстуры: подготовка и очистка данных

Фикстуры помогают подготовить окружение для тестов (например, создать временные файлы, подключиться к тестовой БД, настроить мок). Простая фикстура:

import pytest

@pytest.fixture
def sample_data():
    return [1, 2, 3]

def test_using_fixture(sample_data):
    assert sum(sample_data) == 6

Фикстуры можно задавать с различной областью видимости: function, module, class, session — это управляет временем жизни фикстуры.

Полезные приёмы и шпаргалка

Шпаргалка команд:

pytest              # запуск всех тестов
pytest -q           # короткий вывод
pytest tests/test_mod.py::test_name  # запуск одного теста
pytest -k 'expression'  # запуск тестов по выражению в имени
pytest -m slow      # запуск помеченных тестов
pytest --maxfail=1  # остановиться после первой ошибки

Метки и контроль: используйте @pytest.mark.skip и @pytest.mark.xfail для временного пропуска или пометки известных проблем.

Критерии приёмки для add_numbers

  • Функция должна возвращать сумму числовых аргументов.
  • При передаче None или несовместимых типов должен быть поднят TypeError.
  • При передаче целых и плавающих чисел поведение должно соответствовать математическому сложению.

Критерии приёмки можно формализовать в виде набора тестов, которые выполняются в CI при каждом пулл-реквесте.

Примеры тест-кейсов (edge cases)

  • Обычные целые числа: (2, 3) → 5
  • Отрицательные и положительные: (-1, 1) → 0
  • Ноль и ноль: (0, 0) → 0
  • Плавающие точки: (1.5, 2.5) → 4.0
  • Типы несовместимы: (None, 2) → TypeError
  • Строки: (“2”, 3) → чаще всего TypeError или конкатенация — уточните требуемое поведение

Когда Pytest может не подойти

  • Очень лёгкие сценарии, где хватает assert-ов в ad-hoc скриптах — Pytest может быть избыточен.
  • Очень специфичные среды с ограничениями на запуск внешних процессов — требуется адаптация фикстур.
  • Если в проекте уже используется другой фреймворк с большим количеством интеграционных тестов и миграция невозможна.

Альтернатива: unittest (встроенный модуль) или nose2 — но Pytest обычно проще и гибче.

Интеграция в CI (пример для GitHub Actions)

Файл workflow (.github/workflows/python-tests.yml):

name: CI

on: [push, pull_request]

jobs:
  tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Set up Python
        uses: actions/setup-python@v2
        with:
          python-version: '3.10'
      - name: Install dependencies
        run: |
          python -m venv .venv
          source .venv/bin/activate
          pip install -U pip
          pip install pytest
      - name: Run tests
        run: pytest -q

Этот пример показывает базовую установку окружения и запуск тестов. Для Windows runner команды активации нужно поменять.

Best practices и паттерны

  • Держите тесты небольшими и атомарными — один утверждение на поведение.
  • Используйте параметризацию для наборов входных данных.
  • Фикстуры применяйте для подготовки окружающей среды и повторно используемых ресурсов.
  • Избегайте логики в тестах — тест должен проверять поведение, а не выполнять вычисления, которые могут повторять баг.
  • Пишите названия тестов так, чтобы по имени было понятно, что проверяется.

Роль‑ориентированные чек-листы

Для разработчика:

  • Созданы тесты для новых функций
  • Добавлены тесты на граничные случаи
  • Тесты проходят локально и в CI

Для ревьювера:

  • Тест покрывает бизнес‑логику, а не реализацию
  • Нет избыточных зависимостей в тестах
  • Сообщения об ошибках информативны

Маленькая методология: как писать тесты для новой функции

  1. Определите ожидаемое поведение и граничные случаи.
  2. Напишите тесты, которые сначала падают (red).
  3. Реализуйте минимальное исправление (green).
  4. Рефакторьте код и тесты (refactor).

Эта простая «Red‑Green‑Refactor» практика ускоряет разработку и поддерживает покрытие тестами.

Шпаргалка по плагинам и расширениям

  • pytest-cov — отчёты покрытия кода
  • pytest-mock — удобная обёртка для mock
  • pytest-xdist — параллельный запуск тестов

Используйте плагины, когда проект требует дополнительной функциональности.

Краткий глоссарий

  • Фикстура — функция подготовки окружения для теста.
  • Параметризация — запуск одного теста с разными входными данными.
  • Mark — метка для теста, например slow или integration.
  • xfail — ожидаемый провал теста.

Заключение

Pytest — мощный и гибкий инструмент для тестирования Python‑кода. Начав с простых assert и параметризации, вы быстро сможете перейти к фикстурам, запуску тестов в CI и использованию плагинов. Следуйте простым практикам: маленькие тесты, явные критерии приёмки, повторно используемые фикстуры.

Notes: начните с простого набора тестов для ключевых функций, затем расширяйте покрытие по мере роста кода.

Сводка и рекомендации

  • Начните с виртуального окружения и установки pytest.
  • Пишите как можно более простые и понятные тесты.
  • Используйте pytest.raises для проверки исключений.
  • Параметризуйте тесты для набора входных данных.
  • Включите прогон тестов в CI и добавьте базовое покрытие.

Дополнительные ресурсы: официальная документация Pytest и плагины для покрытия кода и упрощения мокирования.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

RDP: полный гид по настройке и безопасности
Инфраструктура

RDP: полный гид по настройке и безопасности

Android как клавиатура и трекпад для Windows
Гайды

Android как клавиатура и трекпад для Windows

Советы и приёмы для работы с PDF
Документы

Советы и приёмы для работы с PDF

Calibration в Lightroom Classic: как и когда использовать
Фото

Calibration в Lightroom Classic: как и когда использовать

Отключить Siri Suggestions на iPhone
iOS

Отключить Siri Suggestions на iPhone

Рисование таблиц в Microsoft Word — руководство
Office

Рисование таблиц в Microsoft Word — руководство