Как только тестов становится больше одного, появляется повторение: в каждом нужно войти, получить токен, собрать заголовки. Скопировать эти три строки в десять тестов — значит потом менять их в десяти местах. Функция решает ровно это: код пишется один раз и вызывается по имени.
Параметры входят, результат выходит через return. Меняется тело функции — меняется поведение во всех тестах сразу.
Как выглядит функция
живой пример
def поздороваться():
print("Привет")
поздороваться() # вызов: теперь напечатается
Запустить
Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Неделя бесплатно →
def — «определяю функцию», дальше имя, скобки и двоеточие. Тело — со сдвигом.
Само определение ничего не делает: пока функцию не вызвали, внутри неё ничего не
происходит.
Параметры: одно и то же, но с разными данными
живой пример
def поздороваться(имя):
print(f"Привет, {имя}")
поздороваться("Анна") # Привет, Анна
поздороваться("Иван") # Привет, Иван
Запустить
Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Неделя бесплатно →
Имя в скобках — параметр: переменная, которая появится внутри функции с тем значением, которое передали при вызове.
Параметров может быть несколько:
живой пример
def стоимость(цена, количество):
print(цена * количество)
стоимость(4990, 2) # 9980
Запустить
Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Неделя бесплатно →
Возврат значения
Печатать изнутри функции почти всегда неправильно: результат нужен не на экране,
а в коде. Для этого return:
живой пример
def стоимость(цена, количество):
return цена * количество
итого = стоимость(4990, 2)
print(итого) # 9980
Запустить
Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Неделя бесплатно →
return немедленно заканчивает функцию и отдаёт значение наружу. Всё, что
написано после него, не выполнится.
Функция может возвращать и ответ «да/нет» — такие особенно нужны в проверках:
живой пример
def оплачен(заказ):
return заказ["status"] == "PAID"
заказ = {"id": "ord-042", "status": "PAID"}
if оплачен(заказ):
print("заказ оплачен")
Запустить
Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Неделя бесплатно →
Значения по умолчанию
живой пример
def найти_заказы(заказы, статус="PAID"):
return [з for з in заказы if з["status"] == статус]
заказы = [
{"id": "ord-042", "status": "PAID", "total": 4990},
{"id": "ord-043", "status": "CANCELLED", "total": 890},
{"id": "ord-044", "status": "PAID", "total": 1780},
]
print(найти_заказы(заказы)) # ищет PAID
print(найти_заказы(заказы, "CANCELLED")) # ищет отменённые
Запустить
Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Неделя бесплатно →
Параметр со значением по умолчанию можно не передавать. Это удобно, когда у функции есть обычный случай и редкие исключения.
Одна ловушка, о которой узнают больно: значением по умолчанию не должен быть список или словарь.
живой пример
def плохо(элемент, куда=[]): # так не делают
куда.append(элемент)
return куда
Запустить
Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Неделя бесплатно →
Такой список создаётся один раз на всю программу и копит значения между
вызовами. Правильно — куда=None, а внутри if куда is None: куда = [].
Зачем это тестировщику
Три типичных применения, которые встретятся в первый же день.
Подготовка данных. Вход, токен, заголовки — в одну функцию:
живой пример
def войти(client):
ответ = client.post("/auth/login", json={"email": "anna@example.com", "password": "secret123"})
return {"Authorization": "Bearer " + ответ.json()["token"]}
Запустить
Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Неделя бесплатно →
Дальше в каждом тесте одна строка вместо трёх. А когда вход изменится, править придётся одно место. В настоящем pytest такая функция становится фикстурой — это следующий шаг, и он совсем небольшой.
Своя проверка. Условие, которое повторяется, выносят и называют по смыслу:
живой пример
def сумма_сходится(заказ):
посчитано = sum(п["qty"] * п["price"] for п in заказ["items"])
return посчитано == заказ["total"]
заказ = {"items": [{"qty": 1, "price": 4990}, {"qty": 2, "price": 890}], "total": 6770}
print(сумма_сходится(заказ)) # True
Запустить
Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Неделя бесплатно →
Читающий видит if not сумма_сходится(заказ) и понимает намерение без разбора
арифметики.
Разбор ответа. Достать из вложенного ответа то, что нужно, — тоже функция: тест остаётся про проверку, а не про навигацию по словарям.
Как называть
Имя функции — глагол или вопрос: войти, найти_заказы, оплачен,
сумма_сходится. В рабочем проекте пишут по-английски и в нижнем регистре через
подчёркивание: login, find_orders, is_paid. Если имя получается длинным
вроде сделать_всё_и_проверить — функция делает слишком много, её стоит
разделить.
Что решать
Банк «Функции и проверки»:
Коротко
def имя(параметры):объявляет функцию; пока её не вызвали, внутри ничего не происходит.returnотдаёт результат наружу и заканчивает функцию; печатать изнутри — почти всегда ошибка.- Значение по умолчанию делает параметр необязательным; списком или словарём оно быть не должно.
- В тестах функции закрывают три задачи: подготовка данных, своя проверка, разбор ответа.
- Имя — глагол или вопрос; слишком длинное имя означает, что функция делает слишком много.
Что почитать дальше
- assert и ошибки — как проверка падает понятно.
- Разбор данных — считать, сверить, найти расхождение.