Зарегистрируйтесь, чтобы продолжить обучение

Фреймворк Pytest Python: Автоматическое тестирование

Когда тестов и файлов с тестами становится много, возникают новые вопросы:

  • Как группировать тесты?
  • Как запустить на выполнение все тесты из одной директории?
  • Если тестов очень много и они долгие, можно ли запустить их параллельно?

Для решения этих вопросов используют специальные тестовые фреймворки. Они помогают организовать структуру тестов и дают много полезного, например, удобный вывод. С большинством из этих возможностей мы познакомимся далее по курсу.

В Python-мире популярен фреймворк Pytest, на котором мы сосредоточимся в этом уроке.

Что такое Pytest

Python — один из немногих языков, в стандартную библиотеку которого уже включен тестовый фреймворк (он называется unittest). Было бы логично рассмотреть его здесь, но он глубоко завязан на классы, которые вы можете не знать. Поэтому здесь мы рассматриваем Pytest, который все равно популярнее встроенного фреймворка.

Далее мы создадим проект с нуля и добавим в него тесты.

Настройка и запуск

Создайте где-нибудь на вашем компьютере директорию с названием hexlet_pytest. Зайдите в нее и выполните команды:

uv init # Создание базовой структуры
uv sync # Установка зависимостей
mkdir tests

Мы инициализируем новый проект и добавляем в него директорию для будущих тестов tests. Установите pytest с помощью uv в девелопмент зависимости с помощью команды uv add --dev pytest. Проверьте, что все работает правильно:

uv run pytest
# Ниже примерный вывод
platform linux -- Python 3.13.0, pytest-8.3.3, pluggy-1.5.0
rootdir: /private/tmp/hexlet_pytest
collected 0 items

======= no tests ran in 0.01s =======

Теперь добавим немного исходного кода. Создайте вложенную директорию hexlet_pytest, где будет храниться код проекта. Создайте в ней файл example.py. Также добавьте в pyproject.toml блок:

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

В итоге, у вас должна получиться следующая структура:

.
├── hexlet_pytest
│   └── example.py
├── pyproject.toml
├── README.md
└── uv.lock

Внутри файла запишем код:

# Эта функция переворачивает переданную строку
def reverse(string):
    return string[::-1]

Pytest ожидает, что тесты находятся в директории tests, которую располагают в корне проекта. Внутри этого каталога можно создавать любую структуру — Pytest найдет все тесты, которые там лежат.

Именование файлов с тестами должно быть таким: test_<name>.py. Как правило, в этой записи <name> соответствует имени файла, который тестируется.

Напишем наш первый тест. Создайте файл tests/test_example.py со следующим содержимым:

from hexlet_pytest.example import reverse


def test_reverse():
    assert reverse('Hexlet') == 'telxeH'


def test_reverse_for_empty_string():
    assert reverse('') == ''

Далее мы разберем структуру этого файла, а пока попробуем запустить тест на выполнение:

uv run pytest
platform linux -- Python 3.13.0, pytest-8.3.3, pluggy-1.5.0
rootdir: /private/tmp/hexlet-pytest
collected 2 items

tests/test_example.py ..       [100%]

# Один тест запускается так
# uv run pytest tests/test_example.py

Структура

Давайте еще раз посмотрим на файл с тестом:

from hexlet_pytest.example import reverse

def test_reverse():
    assert reverse('Hexlet') == 'telxeH'

def test_reverse_for_empty_string():
    assert reverse('') == ''

Каждый тест внутри файла — это функция, начинающаяся с префикса test_. Тестовых функций можно делать любое количество, все уже зависит от сложности тестируемого кода. Внутри функций проверки выполняются с помощью инструкции assert.

Несмотря на схожесть со встроенной инструкцией в Python, инструкция assert в Pytest работает немного по-другому. Попробуйте внести ошибку в исходную функцию и запустите тесты заново:

    def test_reverse():
>       assert reverse('Hexlet') == 'telxeH'
E       AssertionError: assert 'Hexlet' == 'telxeH'
E         - telxeH
E         + Hexlet

tests/test_example.py:4: AssertionError

Вывод Pytest содержит не только исходное утверждение, но и результат вызова функции:

  • Плюсом помечается то, что пришло
  • Минусом помечается то, что ожидалось

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

В работе Pytest есть одна особенность. Если понадобится распечатать что-то на экран, то Pytest ничего не выведет. По умолчанию он перехватывает и подавляет весь вывод. Отключить это поведение можно с помощью флага -s:

uv run pytest -s
# Теперь видно все, что выводится с помощью print

Самостоятельная работа

  1. Выполните все шаги из этого урока
  2. Создайте репозиторий hexlet_pytest на GitHub
  3. Залейте код на GitHub

Дополнительные материалы

  1. Pytest
  2. Эталонный пакет
  3. Курс "Python: Настройка окружения"

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
от 25 000 ₸ в месяц
Разработка веб-приложений на Django
10 месяцев
с нуля
Старт 26 декабря

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»