Unit tests
Simple unit test
import pytest
def test_some():
assert 1 == 1
Async unit test
Надо поставить плагин для pytest — pytest-asyncio
import pytest
@pytest.mark.asyncio
async def test_some():
assert 1 == 1
Test 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