Параметризованные тесты в Pytest: обзор с примерами

от автора

Привет, Хабр!

Хотите сделать процесс тестирования более эффективным и покрыть больше случаев с меньшим количеством кода? Тогда параметризованные тесты в 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 за несколько строчек кода. Если интересно, записывайтесь по ссылке.


ссылка на оригинал статьи https://habr.com/ru/articles/843142/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *