Привет, Хабр!
Хотите сделать процесс тестирования более эффективным и покрыть больше случаев с меньшим количеством кода? Тогда параметризованные тесты в Pytest — именно то, что вам нужно. В этой статье мы разберёмся, как с помощью параметризации можно существенно ускорить и упростить тестирование вашего приложения.
Сначала установим Pytest:
pip install pytest
Синтаксис
Прежде чем перейти к параметризации, рассмотрим основные фичи Pytest:
Автоматическое обнаружение тестовых файлов и функций: Pytest автоматически находит файлы, начинающиеся с
test_
или заканчивающиеся на_test.py
, и функции внутри них.Мощные фикстуры: Фикстуры позволяют управлять подготовкой и очисткой тестовой среды.
Поддержка асинхронных тестов: Pytest отлично работает с асинхронным кодом через библиотеки вроде
pytest-asyncio
.Гибкие возможности параметризации: Позволяют запускать тесты с различными наборами данных.
Декоратор @pytest.mark.parametrize
— сердце параметризации в Pytest. Он позволяет запустить один и тот же тест с разными наборами входных данных. Синтаксис довольно прост.
Синтаксис декоратора:
@pytest.mark.parametrize(argnames, argvalues)
def test_function(argnames):
# Тело теста
argnames
: имя или список имён параметров, которые будут использоваться в тестовой функции.argvalues
: список значений или кортежей значений, соответствующих параметрам.
Пример с одним параметром:
import pytest
@pytest.mark.parametrize("number", [1, 2, 3, 4, 5])
def test_is_positive(number):
assert number > 0
Тест test_is_positive
будет запущен 5 раз с разными значениями number
.
Параметризация с несколькими аргументами
Pytest позволяет параметризовать тесты с несколькими аргументами, что удобно при тестировании функций с несколькими входными параметрами.
Синтаксис для нескольких аргументов:
@pytest.mark.parametrize("arg1, arg2, arg3", [
(val1_1, val2_1, val3_1),
(val1_2, val2_2, val3_2),
# ...
])
def test_function(arg1, arg2, arg3):
# Тело теста
Пример тестирования функции сложения:
import pytest
@pytest.mark.parametrize("a, b, expected_sum", [
(1, 2, 3),
(5, 5, 10),
(-1, 1, 0),
(0, 0, 0),
])
def test_addition(a, b, expected_sum):
assert a + b == expected_sum
Тест test_addition
будет запущен 4 раза с различными комбинациями a
, b
и expected_sum
.
Параметризация с использованием сложных структур данных
Можно передавать более сложные структуры данных в качестве параметров, такие как списки или словари.
Пример с использованием словарей:
import pytest
test_data = [
{"input": [1, 2, 3], "expected": 6},
{"input": [0, 0, 0], "expected": 0},
{"input": [-1, -2, -3], "expected": -6},
]
@pytest.mark.parametrize("data", test_data)
def test_sum_list(data):
assert sum(data["input"]) == data["expected"]
Здесь передаём словарь data
в тестовую функцию.
Параметризация с фикстурами
Также можно комбинировать параметризацию с фикстурами для более сложных сценариев тестирования.
Пример:
import pytest
@pytest.fixture
def base_number():
return 10
@pytest.mark.parametrize("increment, expected", [
(1, 11),
(2, 12),
(5, 15),
])
def test_increment(base_number, increment, expected):
assert base_number + increment == expected
Здесь фикстура base_number
предоставляет базовое число, а параметризация обеспечивает различные значения для increment
и expected
.
Читаемость тестов с параметр ids
Чтобы сделать выводы тестов более читаемыми, можно использовать параметр ids
в декораторе @pytest.mark.parametrize
.
Пример:
import pytest
@pytest.mark.parametrize("username, password", [
("user1", "pass1"),
("user2", "pass2"),
("admin", "adminpass"),
], ids=["User One", "User Two", "Administrator"])
def test_login(username, password):
# Тест логина с заданными учетными данными
pass
Теперь при запуске тестов в выводе будут отображаться понятные идентификаторы тестовых случаев.
Параметризация класса
Можно параметризовать не только отдельные функции, но и целые классы или модули с тестами.
Пример:
import pytest
test_values = [1, 2, 3]
@pytest.mark.parametrize("number", test_values)
class TestNumberOperations:
def test_is_positive(self, number):
assert number > 0
def test_is_integer(self, number):
assert isinstance(number, int)
В этом случае каждый тестовый метод внутри класса TestNumberOperations
будет выполнен для каждого значения number
.
Параметризация фикстур
Иногда может потребоваться параметризовать фикстуру. Pytest позволяет это сделать с помощью декоратора @pytest.fixture
с параметром params
.
Пример:
import pytest
@pytest.fixture(params=[("user1", "pass1"), ("user2", "pass2")])
def user_credentials(request):
return request.param
def test_login(user_credentials):
username, password = user_credentials
# Логика теста с использованием username и password
pass
Параметризация с использованием indirect
Если нужно передать параметр в фикстуру, можно использовать параметр indirect
.
Пример:
import pytest
@pytest.fixture
def user_profile(username):
# Фикстура, которая создает профиль пользователя на основе имени
return {"username": username, "active": True}
@pytest.mark.parametrize("username", ["alice", "bob"], indirect=True)
def test_user_profile(user_profile):
assert user_profile["active"] is True
Параметр username
передается в фикстуру user_profile
.
Заключение
Параметризация в Pytest — это мощный инструмент, который позволяет гибко настраивать тесты для различных наборов данных и сценариев.
Подробнее с возможностями библиотеки Pytest вы можете ознакомиться по официальной документации.
25 сентября в рамках курса «Python Developer. Professional» пройдет открытый урок на тему «Django Class Based Views». После участия в уроке вы сможете легко и быстро создавать свои представления на основе классов в Django за несколько строчек кода. Если интересно, записывайтесь по ссылке.