Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate event_loop fixture overrides #631

Closed
seifertm opened this issue Oct 9, 2023 Discussed in #587 · 0 comments · Fixed by #632
Closed

Deprecate event_loop fixture overrides #631

seifertm opened this issue Oct 9, 2023 Discussed in #587 · 0 comments · Fixed by #632
Milestone

Comments

@seifertm
Copy link
Contributor

seifertm commented Oct 9, 2023

Discussed in #587

Originally posted by seifertm July 18, 2023

Motivation

pytest-asyncio provides an asyncio event loop via the event_loop fixture. The fixture is function-scoped by default. This is useful for unit testing as it ensures a high level of isolation between test cases. However, as soon as users want to write larger tests that span multiple test cases the scope of the event_loop fixture needs to be extended.

Extending the scope of the event_loop fixture is currently achieved by reimplementing the fixture with an appropriate scope. This approach leads to code duplication, because users are forced to implement the fixture body over and over again. Users are also known to modify the fixture implementation and extend it with custom setup or teardown code.

Several problems arise from this: For one, changes to the fixture implementation in pytest-asyncio would require all users to change their fixture implementations accordingly. For another, requiring users to implement their own event_loop fixture somewhat defeats the purpose of pytest-asyncio in the first place.

Proposed solution

Pytest nodes are discovered by a hierarchy of collectors. Pytest marks can be located at different levels of this hierarchy (see Marking whole classes or modules).

The proposed solution is to derive the scope of the asyncio event loop from the location of the asyncio mark. Consider the following examples.

Strict mode

Test functions

@pytest.mark.asyncio
async def test_a():
    ...

@pytest.mark.asyncio
async def test_b():
    ...

Each function test_a and test_b have their own asyncio mark. Therefore, the event loop scope will be limited to each test function.

Test classes

When using test classes the scope of the event loop can be per-class or per-function, depending on the location of
the asyncio mark.

class MyTestsInFunctionScopedLoop:
    @pytest.mark.asyncio
    async def test_a(self):
        ...

    @pytest.mark.asyncio
    async def test_b(self):
        ...

The functions test_a and test_b each run in their own loop, because the asyncio marker is directly attached to them.

@pytest.mark.asyncio
class MyTestsInClassScopedLoop:
    async def test_a(self):
        ...

    async def test_b(self):
        ...

The functions test_a and test_b run in a common loop, because the asyncio marker is attached to the class.

Module-scoped loops

If the entire module has the asyncio mark all tests are run in a module-scoped event loop.

pytestmark = pytest.mark.asyncio

async def test_a():
    ...

class MyTests:
    async def test_b(self):
        ...

Auto mode

Function scope

async def test_a():
    ...

Class scope

@pytest.mark.asyncio
class MyTests:
    async def test_a(self):
        ...

Module scope

pytestmark = pytest.mark.asyncio

async def test_a(self):
    ...

Session scope

pytestmark = pytest.mark.asyncio # in root conftest.py

async def test_a(self):
    ...

Considered alternatives

Dynamic fixture scopes

pytest-dev/pytest#6101

Introducing dynamic fixture scope in pytest is more effort compared to the proposed solution as it introduces new features to pytest.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant