Руководство по Pytest: как тестировать код в Python
hard

Руководство по Pytest: как тестировать код в Python

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

Мы уже написали много разных проектов на Python. Сегодня перейдём на новый уровень и разберёмся, как написать для них проверочные тесты. 

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

Писать тесты обычно долго и дорого, поэтому есть много разных инструментов для ускорения и упрощения этого процесса. Один из них — Pytest. Про него и поговорим.

Что такое Pytest

Pytest — свободно распространяемый популярный фреймворк для написания автотестов на Python. Это профессиональный функциональный инструмент, которым пользуются 7 000 компаний по всему миру в разных отраслях.

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

Поэтому при масштабировании кодовой базы нужен другой способ. Чтобы быть уверенным, что приложения и сервисы работают нормально и ничего не ломается при изменениях, разработчики добавляют в них программы-автотесты. Автотесты запускают основные фрагменты кода при разных условиях и проверяют, всё ли там в порядке.

Преимущества и недостатки Pytest

В Python уже есть встроенная библиотека для тестов, которая называется unittest, но Pytest отличается от неё и других инструментов тестирования. В основном в лучшую сторону, хотя минусы тоже есть.

Плюсы:

  • Простота. Все тесты пишутся просто в виде функций, названия которых начинаются с test_. В unittest нужно использовать классы и методы.
  • Гибкость и расширяемость. Если каких-то возможностей не хватает, то можно подключить дополнительные плагины или написать свои.
  • Читаемость. Тесты, написанные с Pytest, выглядят понятно для их автора и других разработчиков в команде.
  • Мощные возможности. В Pytest проверки можно упрощать, настраивать и частично переиспользовать. Для этого можно пользоваться фикстурами, метками и параметризацией, про которые мы расскажем дальше.
  • Удобство. Тесты легко запускаются одной командой, а если какая-то из проверок не прошла, Pytest показывает подробное сообщение с ошибкой.

Минусы:

  • Изучение может быть сложным. Хотя сами тесты простые и удобные, но для понимания всех возможностей Pytest потребуется время.
  • Несовместим с другими фреймворками. Другие технологии не умеют запускать тесты Pytest, хотя сам он может запускать тесты из остальных фреймворков.

Установка и настройка Pytest

Установить Pytest можно командой pip install -U pytest.

Если используете виртуальную среду разработки IDE, эту команду нужно выполнить на вкладке терминала:

Как писать тесты с использованием Pytest

Настраивать Pytest перед использованием не нужно, можно сразу начинать писать тесты, но сначала следует запомнить основные правила.

Файлы с тестами должны иметь название, которое начинается с test_ или заканчивается на _test.py:

test_math.py

user_test.py

Тестовые функции, то есть сами тесты, должны начинаться с test_:

def test_sum():
assert 2 + 2 == 4

Assert — ключевое слово для проверки условия. Проверка используется в коде, чтобы убедиться, что код работает так, как ожидается. Если условие выполняется, программа идёт дальше. Если нет — выдаёт ошибку AssertionError и останавливается.

Эта строка:

assert 2 + 2 == 4

проверяет условие, что 2 + 2 равняется 4. Такая проверка всегда будет заканчиваться успешно, поэтому программа всегда будет продолжать работать. Но если написать так:

x = 5
# всё нормально, программа идёт дальше
assert x > 0  
# ошибка — x не меньше 0
assert x < 0

То результат будет зависеть от того, что находится в переменной x.

Одна проверка — один тест. Можно сделать так, что одна тестовая функция будет проводить несколько проверок через assert. Но так делать не надо. Если одна из этих проверок упадёт с ошибкой, то весь тест будет считаться как невыполненный.

# такие тесты писать не надо
def test_math():
    assert 2 + 2 == 4
    assert 2 * 3 == 6
    assert 2 ** 3 == 8

Поэтому на каждую проверку нужен отдельный тест:

# правильный пример тестов: 
# на одну проверку — один assert:

# тест 1
def test_sum():
    assert 2 + 2 == 4

# тест 2
def test_multiply():
    assert 2 * 3 == 6

# тест 3
def test_exponent():
    assert 2 ** 3 == 8

Иногда от этого правила можно отойти, если в тесте обязательно проверить сразу несколько условий. Но всё равно удобнее видеть, какой именно тест не прошёл проверку.

Пишем тест-пример

Чтобы написать тест, нужен код для тестирования. Вот простой алгоритм, на основе которого создаются все тесты:

  1. Пишем Python-скрипт с программой, которую будем тестировать.
  2. Создаём тестовый файл, название которого начинается с test_ или заканчивается на _test.py.
  3. Устанавливаем Pytest командой pip install -U pytest в терминале.
  4. Импортируем скрипт в файл с тестами.
  5. Чтобы запустить тесты, вводим в консоли команду pytest.

Теперь на примере.

В файле main.py пишем программу с функцией сложения:

# пишем функцию сложения
def add(a, b):
   return a + b

Создаём файл test_math.py и устанавливаем Pytest в проект. После этого импортируем в него наш файл с функцией и пишем тест, который проверяет результат сложения 2 и 3:

# test_main.py

# импортируем файл с функцией
import main

# пишем тест-функцию
def test_add():
   # сохраняем в переменную результат сложения 2 и 3
   result = main.add(2, 3)
   # проверяем, что 2 + 3 действительно равно 5
   assert result == 5

Всё готово к запуску, открываем терминал и пишем команду pytest. Видим сообщение о старте тестовой сессии и успешно завершённых тестах:

Теперь попробуем поменять условие — будем проверять, что результат сложения равен 7, а не 5:

# пишем тест-функцию
def test_add():
   # сохраняем в переменную результат сложения 2 и 3
   result = main.add(2, 3)
   # проверяем, что 2 + 3 действительно равно 5
   assert result == 7

Получаем сообщение о том, что тест не пройден, и подробное описание ошибки:

Основные возможности Pytest

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

Фикстуры позволяют добавлять в тесты дополнительный код. Например, это полезно, если в нескольких тестах используются одни и те же повторяющиеся действия. Благодаря этому программа тестов становится проще и читаемее.

Метки дают возможность запускать только некоторые тесты. Это инструмент-фильтр.

Параметризация настраивает тесты так, что одну и ту же функцию можно проверить с разными входными данными. Без этого понадобится написать несколько однотипных тестов.

Теперь подробнее о каждой настройке.

Фикстуры

Технически фикстура — обёртка, в которую завёрнута тестовая функция. Это значит, что при запуске теста программа обязательно столкнётся с обёрткой и тем, что внутри, то есть запустит дополнительный код.Возьмём для примера встроенную функцию sum, которая считает сумму, и передадим в неё список чисел. Список создадим в фикстуре:

# импортируем pytest
import pytest

# обозначаем функцию как фикстуру
@pytest.fixture
# пишем саму фикстуру
def sample_list():
   # возвращаем из фикстуры список чисел от 1 до 5
   return [1, 2, 3, 4, 5]

# пишем тестовую функцию и передаём в неё фикстуру
def test_sum(sample_list):
   # проверяем, что сумма чисел, которые возвращает фикстура, равна 15
   assert sum(sample_list) == 15

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

В итоге количество кода сокращается и повышается его читаемость.

Метки тестов

Метки позволяют группировать тесты и запускать только нужные. Например, можно пометить тесты как «медленные», «критические», «интеграционные» и запускать только те, которые нужны в определённый момент времени.Для примера создадим два простых теста и один пометим меткой slow, а другой — меткой critical:

# импортируем pytest
import pytest

# помечаем тестовую функцию меткой support
@pytest.mark.support
# создаём сам тест
def test_heavy_computation():
   # имитируем сложную операцию:
   # создаём последовательность чисел от 1
   # до 999_999 и складываем все эти числа
   write = sum(range(1_000_000))
   # проверяем, что сумма положительная
   assert write > 0

# помечаем тестовую функцию меткой makes
@pytest.mark.makes
# создаём сам тест
def test_login():
   # проверяем, что в списке есть строка "admin"
   assert "admin" in ["user", "admin", "guest", "easy"]

Теперь можно делать раздельные проверки.

Запускаем все тесты, кроме тех, которые помечены support:

pytest -m "not support"

Результат в консоли — один запущенный тест:

Теперь сделаем по-другому. Запустим только те тесты, которые помечены как makes:

pytest -m makes

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

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

Параметризация

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

Как это выглядит:

  • Перед тестовой функций нужно создать объект @pytest.mark.parametrize().
  • В скобках параметризации сначала нужно передать строку с названиями аргументов. Например, "a, b, expected".
  • После названия аргументов нужно передать список, внутри которого будут храниться кортежи с разными значениями аргументов.

Смотрим на примере:

# импортируем pytest
import pytest

# задаём параметризацию: передаём строку аргументов в кавычках,
# список в квадратных скобках,
# кортежи значений в круглых скобках
@pytest.mark.parametrize("a, b, use", [
   (1, 2, 3),
   (2, 3, 5),
   (-1, 1, 0),
   (0, 0, 0),
])
# создаём саму тестовую функцию
def test_add(a, b, use):
   # проверяем, что сумма значений a и b равна use:
   assert a + b == use

При выполнении этого кода Pytest автоматически создаст 4 теста, по количеству кортежей. Это заменяет 4 отдельных теста с assert.

Кто этим занимается

В зависимости от целей автотесты на Pytest пишут разработчики, тестировщики и DevOps-инженеры.

  • Разработчики проверяют логику кода, обработку ошибок или вообще пишут сначала тесты, а потом сам код. Это называется «разработка через тестирование».
  • Тестировщики проверяют работу системы в разных сценариях и запускают тесты перед релизами каждой версии сервисов и приложений.
  • DevOps-инженеры настраивают CI/CD-пайплайны для автоматического запуска тестов, следят, чтобы новый код не ломал уже существующую кодовую базу.

В следующий раз возьмём один из наших готовых проектов и напишем свои автотесты для его проверки.

Обложка:

Алексей Сухов

Корректор:

Елена Грицун

Вёрстка:

Кирилл Климентьев

Соцсети:

Юлия Зубарева

Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.
Вам может быть интересно
hard
[anycomment]
Exit mobile version