Как тестировать асинхронный код в Python

«Как тестировать асинхронный код в Python» — вопрос из категории Тестирование, который задают на 10% собеседований Python Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Для тестирования асинхронного кода на Python требуются специальные инструменты, так как стандартные test-runner'ы не умеют управлять циклом событий (event loop), необходимым для выполнения async/await функций.

Самый популярный подход — использование библиотеки pytest-asyncio.

1. Написание асинхронного теста с pytest

Тестовая функция должна быть определена с async def и отмечена декоратором @pytest.mark.asyncio. Это указывает pytest-asyncio на необходимость запустить для этого теста event loop.

import pytest

# Функция, которую мы тестируем
async def fetch_data():
    # ... какой-то асинхронный вызов
    return {"data": 123}

@pytest.mark.asyncio
async def test_fetch_data():
    result = await fetch_data()
    assert result == {"data": 123}

2. Использование AsyncMock для мокирования

Для имитации асинхронных объектов используется unittest.mock.AsyncMock, так как его можно вызывать через await, в отличие от стандартного Mock.

from unittest.mock import AsyncMock

@pytest.mark.asyncio
async def test_with_mock():
    # Создаем мок, который можно 'await'-ить
    mock_api_call = AsyncMock(return_value=42)

    result = await mock_api_call()

    mock_api_call.assert_awaited_once()
    assert result == 42

Ключевые моменты:

  • Декоратор: Всегда помечайте асинхронные тесты (@pytest.mark.asyncio).
  • Моки: Используйте AsyncMock или асинхронные аналоги из других библиотек.
  • Фикстуры: Асинхронные фикстуры также должны быть async def и использовать декоратор @pytest_asyncio.fixture.