web-dev-qa-db-fra.com

Appareils asynchrones avec pytest

Comment définir des appareils asynchrones et les utiliser dans des tests asynchrones?

Le code suivant, tous dans le même fichier, échoue lamentablement. L'appareil est-il appelé clairement par le testeur et n'est-il pas attendu?

@pytest.fixture
async def create_x(api_client):
    x_id = await add_x(api_client)
    return api_client, x_id

async def test_app(create_x, auth):
    api_client, x_id = create_x
    resp = await api_client.get(f'my_res/{x_id}', headers=auth)
    assert resp.status == web.HTTPOk.status_code

produire

==================================== ERRORS ====================================
_____________ ERROR at setup of test_app[pyloop] ______________

api_client = <aiohttp.test_utils.TestClient object at 0x7f27ec954f60>

    @pytest.fixture
    async def create_x(api_client):
>       x_id = await add_x(api_client)
...
... cannot show the full trace and pathnames sorry
...    

in __await__
    ret = yield from self._coro /home/mbb/.pyenv/versions/3.6.3/envs/mr/lib/python3.6/site-packages/aiohttp/test_utils.py:245: in request
    method, self.make_url(path), *args, **kwargs /home/mbb/.pyenv/versions/mr/lib/python3.6/site-packages/aiohttp/helpers.py:104: in __iter__
    ret = yield from self._coro /home/mbb/.pyenv/versions/mr/lib/python3.6/site-packages/aiohttp/client.py:221: in _request
    with timer:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <aiohttp.helpers.TimerContext object at 0x7f27ec9875c0>

    def __enter__(self):
        task = current_task(loop=self._loop)

        if task is None:
>           raise RuntimeError('Timeout context manager should be used '
                               'inside a task') E           RuntimeError: Timeout context manager should be used inside a task

/home/mbb/.pyenv/versions/mr/lib/python3.6/site-packages/aiohttp/helpers.py:717: RuntimeError
=========================== 1 error in 1.74 seconds ============================ Process finished with exit code 0

Je sais que je pourrais probablement faire

@pytest.fixture
def create_x(loop, api_client):
    x_id = loop.run_until_complete(add_x(api_client))
    return api_client, x_id

mais j'aimerais savoir s'il existe un moyen plus simple/plus élégant. Je ne trouve pas d'exemple/d'explication clair et simple dans les pages de projet de pytest, pytest-asyncio, pytest-aiohttp.

J'utilise Python 3.6.3, pytest 3.4.2, pytest-asyncio 0.8.0 et pytest-aiohttp 0.3.0

Merci beaucoup pour votre aimable aide

10
Pynchia

Il vous suffit de marquer vos tests comme asynchrones

@pytest.mark.asyncio
async def test_app(create_x, auth):
    api_client, x_id = create_x
    resp = await api_client.get(f'my_res/{x_id}', headers=auth)
    assert resp.status == web.HTTPOk.status_code

Cela indique à pytest d'exécuter le test dans une boucle d'événements plutôt que de l'appeler directement.

Les luminaires peuvent être marqués comme normaux

@pytest.fixture
async def create_x(api_client):
    x_id = await add_x(api_client)
    return api_client, x_id
21
dirn

Les fonctions Coroutine ne sont pas prises en charge nativement par PyTest, vous devez donc installer un cadre supplémentaire pour cela

  • pytest-aiohttp
  • pytest-asyncio
  • pytest-trio
  • pytest-tornasync

Si vous utilisez pytest-aiohttp, votre problème se résout de cette façon

import asyncio
import pytest

from app import db


url = 'postgresql://postgres:postgres@localhost:5432'


@pytest.fixture(scope='session')
def loop():
    return asyncio.get_event_loop()


@pytest.fixture(scope='session', autouse=True)
async def prepare_db(loop):
    async with db.with_bind(f'{url}/postgres') as engine:
        await engine.status(db.text('CREATE DATABASE test_db'))

    await db.set_bind(f'{url}/test_db')
    await db.gino.create_all()

    yield
    await db.bind.close()

    async with db.with_bind(f'{url}/postgres') as engine:
        await engine.status(db.text('DROP DATABASE test_db'))

L'idée principale est d'utiliser le synchronisme loop-fixture qui sera utilisé par les appareils asynchrones