Lors que vous avez une fonction de test qui utilise
asyncio

Avec comme message
got Future <Future pending> attached to a different loop
Bien évidemment de manière unitaire les tests passent avec succès.
Je vais vous expliquer comment résoudre ce problème et quelle est la meilleure solution.
Revenons un peu sur le message
different looploop
event loopLe plugin pytest-asyncio qui est installé dans votre projet attache votre test à l'event loop qu'
asyncio
Copier le code
1 2 3 4 5 6 7 8 9 10 11 # conftest.py @pytest_asyncio.fixture(name="database", scope="session") async def fixture_database(): await create_database() yield await cleanup_database() # test_css.py @pytest.mark.asyncio async def test_css(database): assert True
De ce fait on identifie que la configuration de pytest-asyncio est défaillante car notre
assert Truedatabase
En effet par défaut ce plugin donne un scope
functiondatabase
got Future <Future pending> attached to a different loop
Il faut bien distinguer le scope de la fixture (qui est la durée de vie de la fixture) avec le scope de l'event loop qui est son exécution dans la boucle évènementielle.
Il faut donc modifier le scope de l'event loop à un niveau supérieur à
functionsession
Pour cela il suffit simplement d'ajouter l'option
scope_loop
Copier le code
1 2 3 @pytest.mark.asyncio(loop_scope="session") async def test_css(database): assert True
Les tests passent maintenant mais vous obtenez un warning de ce type :
PytestDeprecationWarning: The configuration option "asyncio_default_fixture_loop_scope" is unset.
The event loop scope for asynchronous fixtures will default to the fixture caching scope. Future versions of pytest-asyncio will default the loop scope for asynchronous fixtures to function scope. Set the default fixture loop scope explicitly in order to avoid unexpected behavior in the future. Valid fixture loop scopes are: "function", "class", "module", "package", "session"
Il faut donc mettre en place cette configuration dans le fichier pyproject.toml
Copier le code
1 2 3 4 # pyproject.toml ... [tool.pytest.ini_options] asyncio_default_fixture_loop_scope = "session"
Et maintenant tout est rentré dans l'ordre tous les tests passent sans warning
Et si on veut ajouter l'option loop_scope par défaut ?
On peut imaginer ne pas vouloir toujours préciser le
loop_scope
session
pytest.mark.asyncio
pytest
Copier le code
1 2 3 4 5 6 7 # conftest.py def pytest_collection_modifyitems(items): """https://pytest-asyncio.readthedocs.io/en/v0.24.0/how-to-guides/run_session_tests_in_same_loop.html""" pytest_asyncio_tests = (item for item in items if is_async_test(item)) session_scope_marker = pytest.mark.asyncio(loop_scope="session") for async_test in pytest_asyncio_tests: async_test.add_marker(session_scope_marker, append=False)
Et maintenant on a juste à préciser le plugin
asyncio
loop_scope
pytest
Il est important de mettre tout de même
@pytest.mark.asyncio
pytest
is_async_test()Conlusion
Conlusion
- Je vous préconise de passer par le décorateur sur vos fonctions de tests. Cette option permet de choisir directement sur le test le scope de l'event loop en fonction des usages de votre
@pytest.mark.asyncio(loop_scope="session")
. Par exemple sur un jeu de donnée pour un test d'intégration le scope de lafixture
serait plutôt enfixture
.fonction
- Je recommande moins la modification en globale car le comportement est caché danset est moins visible en lisant les fonctions de test. Et une fois que vous appliquez ce hook la modification sera effectuée sur TOUTES les fonctions de test
conftest.py