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

Event loop is closed when using pytester and pytest_asyncio.fixture(autouse=True) #291

Closed
unmade opened this issue Feb 13, 2022 · 6 comments
Labels

Comments

@unmade
Copy link

unmade commented Feb 13, 2022

If you have an async fixture with a teardown and autouse=True and you have a simple non-async test with a pytester plugin, then that fixture is always added as a finalizer.

Basically, if you have the following:

# tests/conftest.py

@pytest.fixture(autouse=True)
async def teardown(request):
    try:
        yield
    finally:
        pass


# tests/test_fixtures.py

def test_fixture_setup(pytester):
    # create a temporary conftest.py file
    pytester.makeconftest("""
        pytest_plugins = ["tests.conftest"]
    """)

    # create a temporary pytest test file
    pytester.makepyfile("""
        def test_setup():
            assert True
    """)

    result = pytester.runpytest("--asyncio-mode=auto")
    result.assert_outcomes(passed=1)

Then, the test fails on a teardown with the following error:

ERROR tests/test_fixtures.py::test_fixture_setup - RuntimeError: Event loop is closed

I've checked it on pytest-asyncio==0.16.0 and pytest>=0.17.0. However, prior v0.17.0 there was a workaround - you could redefine an event_loop in a temporary pytest test file without closing the loop. Since, v0.17.0 this is no longer possible.
In v0.17.0 the only workaround is to override this async fixture itself in temporary pytest test file.


I made a minimal reproducible example, you can find it here:

https://github.com/unmade/pytester-pytest-asyncio-bug

@romainletendart
Copy link

romainletendart commented Feb 25, 2022

Hi all,
I get similar issues on my tests.
@asvetlov Any ideas? I guess this is linked to d8efa64.

@seifertm seifertm self-assigned this Mar 7, 2022
@seifertm
Copy link
Contributor

seifertm commented Mar 7, 2022

Thanks for the report! I can reproduce the issue with your example.

My current understanding is that the programmatic pytest run using pytester.runpytest causes the current loop provided by the event_loop fixture to close. In other words, pytester.runpytest is not a hermetic environment and affects the event_loop fixture of the test using pytester. Please correct me, if I'm wrong.

Modifying the event_loop fixture scope will not change anything, probably because pytester.runpytest represents a full test session.

The example repository presents two workarounds. One workaround is to redefine the event_loop fixture inside pytester.makepyfile and avoid closing the event loop. This workaround no longer works as of pytest-asyncio v0.18.
The second workaround involves redefining the teardownfixture in pytester.makepyfile so that it is no longer an async generator. That way, the fixture does not require finalization and will not trigger the error.

Both are workarounds, so I don't think it makes sense to revert to an earlier version of pytest-asyncio.

This is a tricky one to solve… Ideally, we would like pytester runs to have their own instances of event loop. If we had switched to aioloop-proxy (#235) we could easily spawn a "nested loop" for these type of tests. At the moment, I think this is the go-to solution.

An alternative is to whip up some custom logic specifically for tests requesting the pytester fixture. I have the feeling this will open up a can of worms, though, and cause more harm than it will do good.

I'm sorry that I don't have any immediate fix for this. At the moment, I'd say this issue is blocked by #235. If you have any suggestions, I'd be happy to hear them.

@seifertm seifertm removed their assignment Mar 7, 2022
@seifertm
Copy link
Contributor

seifertm commented Sep 6, 2022

@unmade Does it solve the issue if you use pytester.runpytest_subprocess rather than pytester.runpytest?

@seifertm
Copy link
Contributor

I can no longer reproduce the issue using the code in the linked repository with pytest-asyncio 0.19.0. I checked CPython 3.7 up to v3.10. Please reopen if you happen to run into this problem again.

@seifertm seifertm added the bug label Sep 16, 2022
@crypto-only
Copy link

crypto-only commented Sep 30, 2022

How to simplify this in async test?
Such as

def test_get(pytester):
    pytester.makepyfile(
        """\
        async def test_get():
            await get()
        """
    )
    pytester.runpytest_subprocess("--asyncio-mode=auto")

@unmade Does it solve the issue if you use pytester.runpytest_subprocess rather than pytester.runpytest?

@seifertm
Copy link
Contributor

@crypto-only You'd only use pytester if you want to run pytest inside a test. This can be handy when you write a pytest plugin and want to test different configurations of the plugin.

Your example can be simplified by setting asyncio_mode = auto in pytest.ini and simply writing:

async def test_get():
    await get()

There is no need to use pytester in your example.

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

No branches or pull requests

4 participants