Эффективные тесты на Python: 5 советов

Структура тестов должна соответствовать структуре приложения

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

Представьте, что в вашем приложении есть компоненты, такие как модули, интеграции, сущности и API. Будет чудесно, если структура тестов соответствует структуре приложения. Такой подход сильно облегчает поиск нужных тестов, добавление новых, и поддержание порядка в тестах.

Например:

```
└── project
 ├── tests
 │ ├── __init__.py
 │ ├── conftest.py
 │ ├── fixtures
 │ │ └── api.py
 │ ├── helpers.py
 │ └── web
 │ ├── __init__.py
 │ ├── http
 │ │ ├── __init__.py
 │ └── jsonrpc
 │ ├── __init__.py
 │ └── api_v1
 │ ├── __init__.py
 | ├── test_increase_my_salary.py
 └── web
 ├── __init__.py
 …
```

В такой структуре вы всегда точно знаете, где вставить новый метод JsonRpc и как быстро найти нужные тесты.

Названия тестов соответствуют API-методам

Вместо того чтобы создавать один main-файл теста, назовите свои тестовые файлы именами соответствующих методов API. Например, если у вас есть метод JsonRpc с именем «increase_my_salary», создайте файл с именем «test_increase_my_salary» в каталоге «jsonrpc». Таким образом, сама структура объясняет, какие тесты здесь, без необходимости открывать каждый файл.

Один тест — один кейс

Каждый тест должен быть сфокусирован на одном действии, которое может быть неудачным. Лучше не пишите тесты, в которых вызывается API для создания объекта, а затем используется это API. Это проблемно, поскольку при создании объекта могут и будут возникать ошибки, что приведет к ненадежности тестов. Убедитесь, что каждый тест направлен на проверку вокруг одного вызова/действия. Это облегчает понимание, отслеживание и устранение проблем.

Пример:

async def test_salary_increase_with_valid_employee_id():
    # Arrange: Prepare the necessary data or context
    employee_id = ...  # Replace with a valid employee ID
    
    # Act: Perform the action you're testing
    result = await jsonrpc_api_v1('increase_my_salary', id=employee_id)
    
    # Assert: Check the expected outcome
    assert ...  # Add your assertion for the expected result

Фикстуры

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

# fixtures.py

import pytest

@pytest.fixture
async def create_account() -> Account:
    return await Account.create(...)

@pytest.fixture
async def create_employee(create_account) -> Employee:
    return await Employee.create(account=create_account, ...)

# test_increase_my_salary.py

async def test_salary_increase_request(create_employee):
    employee = create_employee
    result = await jsonrpc_api_v1('increase_my_salary', id=employee.id)
    assert ...

Здесь мы создали фикстуру с именем «employee» и инкапсулировали ее зависимость от другого объекта account, который также определен как фикстура. При запуске теста Pytest автоматически управляет зависимостями и создает нужные объекты.

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

Внимательно с данными

Иногда в рамках одного теста могут понадобиться различные объекты с разными свойствами. Вместо того чтобы создавать множество фикстур типа «employee_1» или «employee_1«, используйте фабрики. Вы можете определить функцию внутри фикстуры для создания объектов и связей между ними.

# fixtures.py

import pytest

@pytest.fixture
def create_account_factory() -> Account:
    async def create_account(**kwargs):
        return await Account.create(**kwargs)
    return create_account

@pytest.fixture
def create_employee_factory(create_account_factory) -> Employee:
    async def create_employee(employee_kwargs: dict, account_kwargs: dict):
        account = await create_account_factory(**account_kwargs)
        return await Employee.create(account=account, **employee_kwargs)
    return create_employee

# test_send_message.py
async def test_send_message_between_employees(create_employee_factory):
    # Arrange: Create employees with specific account IDs and default employees
    employee_1 = await create_employee_factory(
        employee_kwargs={'id': 1},
        account_kwargs={'id': 101}
    )
    employee_2 = await create_employee_factory()

    # Act: Simulate sending a message between employees
    result = await send_message(employee_1, employee_2, "Hello, World!")

    # Assert: Check the expected outcome
    assert result == "Message sent successfully"

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

Фабрики и фикстуры в документации pytest


Источник

Какой была ваша первая зарплата в QA и как вы искали первую работу?

Мега обсуждение в нашем телеграм-канале о поиске первой работы. Обмен опытом и мнения.

Подписаться
Уведомить о
guest

0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии

Мы в Telegram

Наш официальный канал
Полезные материалы и тесты
Готовимся к собеседованию
Project- и Product-менеджмент

? Популярное

? Telegram-обсуждения

Наши подписчики обсуждают, как искали первую работу в QA. Некоторые ищут ее прямо сейчас.
Наши подписчики рассказывают о том, как не бояться задавать тупые вопросы и чувствовать себя уверенно в новой команде.
Обсуждаем, куда лучше податься - в менеджмент или по технической ветке?
Говорим о конфликтных ситуациях в команде и о том, как их избежать
$1100*
медианная зарплата в QA в июне 2023

*по результатам опроса QA-инженеров в нашем телеграм-канале

Собеседование

19%*
IT-специалистов переехало или приняло решение о переезде из России по состоянию на конец марта 2022

*по результатам опроса в нашем телеграм-канале

live

Обсуждают сейчас