13

I think the example below is a really common use case:

  1. create a connection to a database once,
  2. pass this connection around to test which insert data
  3. pass the connection to a test which verifies the data.

Changing the scope of @pytest.fixture(scope="module") causes ScopeMismatch: You tried to access the 'function' scoped fixture 'event_loop' with a 'module' scoped request object, involved factories.

Also, the test_insert and test_find coroutine do not need the event_loop argument because the loop is accessible already by passing the connection.

Any ideas how to fix those two issues?

import pytest

@pytest.fixture(scope="function")  # <-- want this to be scope="module"; run once!
@pytest.mark.asyncio
async def connection(event_loop):
    """ Expensive function; want to do in the module scope. Only this function needs `event_loop`!
    """
    conn await = make_connection(event_loop)
    return conn


@pytest.mark.dependency()
@pytest.mark.asyncio
async def test_insert(connection, event_loop):  # <-- does not need event_loop arg
    """ Test insert into database.

        NB does not need event_loop argument; just the connection.
    """
    _id = 0
    success = await connection.insert(_id, "data")
    assert success == True


@pytest.mark.dependency(depends=['test_insert'])
@pytest.mark.asyncio
async def test_find(connection, event_loop):  # <-- does not need event_loop arg
    """ Test database find.

        NB does not need event_loop argument; just the connection.
    """
    _id = 0
    data = await connection.find(_id)
    assert data == "data"
sanyassh
  • 8,100
  • 13
  • 36
  • 70
Daniel Farrell
  • 9,316
  • 8
  • 39
  • 62
  • don't use the default `event-loop` fixture that they provide, you'll have to make your own and pass it in to your fixture. their default fixture, as stated in the docs is scoped for functions so you won't be able to use it in a `module` fixture like you want – gold_cy May 21 '19 at 10:59
  • Thanks, that put me on the right track. – Daniel Farrell May 21 '19 at 12:32

2 Answers2

22

The solution is to redefine the event_loop fixture with the module scope. Include that in the test file.

@pytest.fixture(scope="module")
def event_loop():
    loop = asyncio.get_event_loop()
    yield loop
    loop.close()
Zev Isert
  • 915
  • 11
  • 20
Daniel Farrell
  • 9,316
  • 8
  • 39
  • 62
  • 2
    Also it is better to have `scope=session`. You will be able to use `event_loop` in other session-scoped fixtures. There are no reasons to have separate loop for each module. – sanyassh May 22 '19 at 12:21
  • That’s for pointing that out, will be useful in some cases. However here `get_event_loop` is returning the default loop for the thread, so it’s not a problem to have that in the module scope (or function scope for that matter!) – Daniel Farrell May 22 '19 at 15:05
5

Similar ScopeMismatch issue was raised in github for pytest-asyncio (link). The solution (below) works for me:

@pytest.yield_fixture(scope='class')
def event_loop(request):
    loop = asyncio.get_event_loop_policy().new_event_loop()
    yield loop
    loop.close()
shrey khare
  • 155
  • 2
  • 6