Unit tests
Simple unit test
import pytest
def test_some():
   assert 1 == 1Async unit test
Надо поставить плагин для pytest — pytest-asyncio
import pytest
@pytest.mark.asyncio
async def test_some():
    assert 1 == 1Test exceptions
import pytest
def test_raise():
    with pytest.raises(TypeError):
        // do something that lead to the TypeError exception
        ... Запустить тест с набором параметров
@pytest.mark.parametrize('summary, owner, done',
                             [('sleep', None, False),
                              ('wake', 'brian', False),
                              ('breathe', 'BRIAN', True),
                              ('eat eggs', 'BrIaN', False),
                              ])
    def test_add_3(summary, owner, done):
        """Демонстрирует параметризацию с несколькими параметрами."""
        task = Task(summary, owner, done)
        task_id = tasks.add(task)
        t_from_db = tasks.get(task_id)
        assert equivalent(t_from_db, task)Тест асинхронного запроса с асинхронным контекстом
Инструменты:
- pytest 
- pytest-mock 
- pytest-asyncio 
- aiohttp 
Пусть есть такой код (делаем асинхронно GET http запрос к API и парсим ответ с помощью pydantic):
import aiohttp
import asyncio
import pydantic
class Response(pydantic.BaseModel):
    id: int
async def fetch_data(url) -> Response:
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            resp = await response.json()
            return pydantic.TypeAdapter(Response).validate_python(resp)
async def main():
    resp = await fetch_data('https://dummyjson.com/products/1')
    print('Product ID', resp.id)
if __name__ == '__main__':
    asyncio.run(main())
Сложность в этом кусочке:
# (1) Первый раз заходим в асинхронный контекст
async with aiohttp.ClientSession() as session:
   # (2) Второй раз заходим в асинхронный контекст для вызова асинхронной функции get (3) 
   async with session.get(url) as response:
      # (4) А тут мы вызываем асинхронную функцию json
      resp = await response.json()Пишем тест:
import pytest
from pytest_mock import MockerFixture
from aiohttp import ClientSession, ClientResponse
from req import fetch_data  # [1]
@pytest.mark.asyncio  # [2]
async def test_exec(mocker: MockerFixture):  # [3]
    response_mock = mocker.MagicMock(spec_set=ClientResponse)  # [4]
    response_mock.__aenter__.return_value.json.return_value = {'id': 123}  # [5]
    session_mock = mocker.MagicMock(spec_set=ClientSession)
    session_mock.get.return_value = response_mock  # [6]
    # Var 1
    mocker.patch.object(ClientSession, '__aenter__', return_value=session_mock)  # [7]
    result = await fetch_data('https://dummyjson.com/products/1')  # [8]
    assert result.id == 123Где:
- [1] — импортируем нашу асинхронную функцию для тестов 
- [2] — проставляем метку для плагина pytest-asyncio, чтобы pytest смог запустить асинхронный код 
- [3] — Для удобства проставляем тип для нашей фикстуры (эту аннотацию поддерживает pytest-mock) 
- [4] — Собираем mock-объект для aiohttp.ClientResponse 
- [5] — Проставляем ответ для вызова асинхронной функции (4), при этом обрати внимание, что ответ получаем из асинхронного контекста, который формировали в (2) 
- [6] — Проставляем ответ для вызова асинхронной функции get — (3) 
- [7] — Проставляем ответ, который получаем при заходе в контекст (1) 
- [8] — Запускаем саму асинхронную функцию, для которой мы наконец-то пропатчили все сайд эффекты 
Last updated