1

I am using pytest with pytest-asyncio to run async tests. What is strange is that these tests only work if there is exactly one of them. For example:

@pytest.mark.asyncio
async def test_foo():
    client = await get_db_connection()
    user = await client.DO_QUERY
    response = await FunctionUnderTest(
        db=client, ...args
    )
    assert response.id is not None


@pytest.mark.asyncio
async def test_error_foo():
    client = await get_db_connection()

    with pytest.raises(MissingRequiredError):
        await FunctionUnderTest(
            db=client, ...args
        )

If I comment out either of those tests, the remaining one will pass, but running both together gives:

RuntimeError: Task <Task pending name='Task-5' coro=<test_error_foo() running at /tests/test_function_under_test.py:44> cb=[_run_until_complete_cb() at /usr/lib/python3.10/asyncio/base_events.py:184]> got Future <Future pending> attached to a different loop

I would have expected pytest-asyncio to create a single event loop and run all the tests sequentially, but this does not seem to work.

Alex
  • 2,270
  • 3
  • 33
  • 65

1 Answers1

0

You'll hopefully get better mileage by using pytest-asyncio-cooperative which is designed around the use of a single event loop for all tests. pytest-asyncio on the other hand uses an event loop per test.

  1. Install:
pip install pytest-asyncio-cooperative
  1. Replace pytest.mark.asyncio with pytest.mark.asyncio_cooperative
@pytest.mark.asyncio_cooperative
async def test_foo():
    client = await get_db_connection()
    user = await client.DO_QUERY
    response = await FunctionUnderTest(
        db=client, ...args
    )
    assert response.id is not None


@pytest.mark.asyncio_cooperative
async def test_error_foo():
    client = await get_db_connection()

    with pytest.raises(MissingRequiredError):
        await FunctionUnderTest(
            db=client, ...args
        )
  1. Run the tests with pytest-asyncio OFF and sequential running of tests ON (ie. --max-asyncio-tasks 1):
pytest -p no:asyncio --max-asyncio-tasks 1 test_function_under_test.py

Admittedly, if you want to keep using pytest-asyncio you could rewrite your tests to use the event loop provided by pytest-asyncio and then explicitly use it in your test. I think you'd have to pass it into your code that is referencing the event loop.

Disclaimer: I am the maintainer of this pytest.mark.asyncio_cooperative

likeaneel
  • 83
  • 6