1

I am using pytest-asyncio.

I have the following conftest.py file:

import asyncio

import pytest
from database.mongo_db import mongo


@pytest.fixture(scope="session", autouse=True)
async def initialise_db():
    await mongo.connect_client()
    await mongo.drop_db()

@pytest.fixture(scope="session")
def event_loop():
    yield asyncio.new_event_loop()

The initialise_db() function will connect to my database and clear everything from it before all of my tests are run.

Now, I want to close the event loop and also close the connection to my database once all tests are complete. I have tried adding the following function to conftest.py:

def pytest_sessionfinish(session, exitstatus):
    asyncio.get_event_loop().close()
    mongo.disconnect_client()

However, this new function has two issues:

  1. asyncio.get_event_loop().close() raises a warning: DeprecationWarning: There is no current event loop
  2. mongo.disconnect_client() is an async function. If I change pytest_sessionfinish to an async function and use await when closing the database, then I get the warning: RuntimeWarning: coroutine 'pytest_sessionfinish' was never awaited, and this is called from within pytest, so I cannot change it to be awaited unless I edit the source code. Of course, if I don't make it an async function I get the warning: RuntimeWarning: coroutine 'disconnect_client' was never awaited.

How can I resolve these two issues?

KOB
  • 4,084
  • 9
  • 44
  • 88

1 Answers1

1
  1. Don't. Pytests manages the event loop and you shouldn't (and needn't) interfere with that. [EDIT: Ok, sorry. Interfere, if you have to... For example to extend the scope of the event_loop fixture.]

  2. Fixtures should take care of their own teardown. Just define it as a yield fixture:

@pytest.fixture(scope="session")
def event_loop():
    loop = asyncio.new_event_loop()
    yield loop
    loop.close()

@pytest.fixture(scope="session", autouse=True)
async def initialise_db():
    await mongo.connect_client()
    await mongo.drop_db()
    yield # suspended until tests are done
    await mongo.disconnect_client() # pytest calls teardown when fixture goes out of scope

The event_loopfixture is provided by default by pytest-asyncio, but can be overridden to change the scope or provide a custom loop. See pytest-asyncio readme for details.

thisisalsomypassword
  • 1,423
  • 1
  • 6
  • 17
  • Thanks, I understand point 1, but when I use your solution given (with `scope="session"`) I get the following error: `ScopeMismatch: You tried to access the 'function' scoped fixture 'event_loop' with a 'session' scoped request object`. For now, I can use this fixture with the default `function` scope, but I think in the future I will have to change the scope to `session` so that my database stays persisted across all tests. – KOB Jan 06 '22 at 12:09
  • You're right, I overlooked the scope issue. I'll edit the answer accordingly. – thisisalsomypassword Jan 06 '22 at 12:18
  • Maybe I misunderstood your comment. You defined an `event_loop` fixture yourself in your example with a "session"-scope. Why does pytest complain, that you defined your event loop with "function"-scope? – thisisalsomypassword Jan 06 '22 at 12:31
  • when I try your updated solution I am getting the following warning: `DeprecationWarning: There is no current event loop` which causes my tests to fail (I presume because no event loop is being returned), hence why my original solution used `asyncio.new_event_loop()`. – KOB Jan 10 '22 at 10:52
  • I didn't get that `DeprecationWarning` since I'm still on Python 3.8... Does it work if you use `new_event_loop()` instead? I can't easily test this right now, but I don't see why it shouldn't work. I'll update the example. – thisisalsomypassword Jan 10 '22 at 11:52