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

Shared tests with hypothesis throws an Event loop is closed error #231

Closed
gryevns opened this issue Oct 20, 2021 · 1 comment · Fixed by #239
Closed

Shared tests with hypothesis throws an Event loop is closed error #231

gryevns opened this issue Oct 20, 2021 · 1 comment · Fixed by #239

Comments

@gryevns
Copy link

gryevns commented Oct 20, 2021

When you have a class containing some shared tests (e.g. for contract testing between several implementations of an interface), and use both hypothesis and pytest-asyncio on the shared tests, pytest will raise an error when running the test for the second time.

HypothesisWorks/hypothesis#3122

Example

import hypothesis
import hypothesis.strategies
import pytest

class Shared:
    @pytest.mark.asyncio
    @hypothesis.given(value=hypothesis.strategies.integers())
    async def test_foo(self, value: int) -> None:
        assert value == value

class TestCaseOne(Shared):
    pass

class TestCaseTwo(Shared):
    pass
================================= test session starts ==================================
platform darwin -- Python 3.9.4, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: /Users/garyevans/Projects/pytest-async-hypothesis
plugins: asyncio-0.16.0, hypothesis-6.23.3
collected 2 items                                                                      

test.py .F                                                                       [100%]

======================================= FAILURES =======================================
_________________________________ TestCaseTwo.test_foo _________________________________

self = <test.TestCaseTwo object at 0x1069abe20>

    @pytest.mark.asyncio
>   @hypothesis.given(value=hypothesis.strategies.integers())

test.py:8: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../Library/Caches/pypoetry/virtualenvs/pytest-async-hypothesis-BRvTXcHz-py3.9/lib/python3.9/site-packages/pytest_asyncio/plugin.py:191: in inner
    coro = func(**kwargs)
../../Library/Caches/pypoetry/virtualenvs/pytest-async-hypothesis-BRvTXcHz-py3.9/lib/python3.9/site-packages/pytest_asyncio/plugin.py:193: in inner
    task = asyncio.ensure_future(coro, loop=_loop)
../../.pyenv/versions/3.9.4/lib/python3.9/asyncio/tasks.py:667: in ensure_future
    task = loop.create_task(coro_or_future)
../../.pyenv/versions/3.9.4/lib/python3.9/asyncio/base_events.py:431: in create_task
    self._check_closed()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <_UnixSelectorEventLoop running=False closed=True debug=False>

    def _check_closed(self):
        if self._closed:
>           raise RuntimeError('Event loop is closed')
E           RuntimeError: Event loop is closed

../../.pyenv/versions/3.9.4/lib/python3.9/asyncio/base_events.py:510: RuntimeError
-------------------------------------- Hypothesis --------------------------------------
Falsifying example: test_foo(
    value=0, self=<test.TestCaseTwo at 0x1069abe20>,
)
=============================== short test summary info ================================
FAILED test.py::TestCaseTwo::test_foo - RuntimeError: Event loop is closed
============================= 1 failed, 1 passed in 0.13s ==============================
sys:1: RuntimeWarning: coroutine 'Shared.test_foo' was never awaited

Environment

Package          Version
---------------- -------
hypothesis       6.23.3
pytest           6.2.5
pytest-asyncio   0.16.0

Workaround

If we modify the event_loop fixture and do not close the loop by default then the tests pass as expected.

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

@AIGeneratedUsername
Copy link

AIGeneratedUsername commented Nov 11, 2021

A workaround by gryevns did not help me.

I've tried to wrap all my fixtures with the with contextlib.suppress(RuntimeError): to at least temporary hide the error, but I still get an exception:

def finalizer():
        """Yield again, to finalize."""
    
        async def async_finalizer():
            try:
                await gen_obj.__anext__()
            except StopAsyncIteration:
                pass
            else:
                msg = "Async generator fixture didn't stop."
                msg += "Yield only once."
                raise ValueError(msg)
    
>       loop.run_until_complete(async_finalizer())

../venv/lib/python3.9/site-packages/pytest_asyncio/plugin.py:139: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/usr/lib/python3.9/asyncio/base_events.py:617: in run_until_complete
    self._check_closed()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <_UnixSelectorEventLoop running=False closed=True debug=True>

    def _check_closed(self):
        if self._closed:
>           raise RuntimeError('Event loop is closed')
E           RuntimeError: Event loop is closed

/usr/lib/python3.9/asyncio/base_events.py:510: RuntimeError

seifertm added a commit to seifertm/pytest-asyncio that referenced this issue Jan 6, 2022
Pytest-asyncio identifies Hypothesis test cases by their `is_hypothesis_test` flag. When setting up an async Hypothesis test pytest-asyncio replaces wraps function's `hypothesis.inner_test` attribute. The the top level function never changes.

When a Hypothesis test case is defined in a base class and inherited by subclasses, the test is collected in each subclass. Since the top-level Hypothesis test never changes, its inner test will be wrapped multiple times.

Double wrapping leads to execution errors caused by stale (closed) event loops in all test executions after the first.

This change adds an `original_test_function` attribute to the async function wrapper, in order to keep track of the original Hypothesis test. When re-wrapping would occur in subclasses pytest-asyncio wraps the original test function rather than the wrapper function.

Closes pytest-dev#231

Signed-off-by: Michael Seifert <m.seifert@digitalernachschub.de>
seifertm added a commit to seifertm/pytest-asyncio that referenced this issue Jan 6, 2022
Pytest-asyncio identifies Hypothesis test cases by their `is_hypothesis_test` flag. When setting up an async Hypothesis test pytest-asyncio replaces the function's `hypothesis.inner_test` attribute. The the top level function never changes.

When a Hypothesis test case is defined in a base class and inherited by subclasses, the test is collected in each subclass. Since the top-level Hypothesis test never changes, its inner test will be wrapped multiple times.

Double wrapping leads to execution errors caused by stale (closed) event loops in all test executions after the first.

This change adds an `original_test_function` attribute to the async function wrapper, in order to keep track of the original Hypothesis test. When re-wrapping would occur in subclasses pytest-asyncio wraps the original test function rather than the wrapper function.

Closes pytest-dev#231

Signed-off-by: Michael Seifert <m.seifert@digitalernachschub.de>
seifertm added a commit to seifertm/pytest-asyncio that referenced this issue Jan 7, 2022
Pytest-asyncio identifies Hypothesis test cases by their `is_hypothesis_test` flag. When setting up an async Hypothesis test pytest-asyncio replaces the function's `hypothesis.inner_test` attribute. The the top level function never changes.

When a Hypothesis test case is defined in a base class and inherited by subclasses, the test is collected in each subclass. Since the top-level Hypothesis test never changes, its inner test will be wrapped multiple times.

Double wrapping leads to execution errors caused by stale (closed) event loops in all test executions after the first.

This change adds an `original_test_function` attribute to the async function wrapper, in order to keep track of the original Hypothesis test. When re-wrapping would occur in subclasses pytest-asyncio wraps the original test function rather than the wrapper function.

Closes pytest-dev#231

Signed-off-by: Michael Seifert <m.seifert@digitalernachschub.de>
seifertm added a commit to seifertm/pytest-asyncio that referenced this issue Jan 7, 2022
Pytest-asyncio identifies Hypothesis test cases by their `is_hypothesis_test` flag. When setting up an async Hypothesis test pytest-asyncio replaces the function's `hypothesis.inner_test` attribute. The the top level function never changes.

When a Hypothesis test case is defined in a base class and inherited by subclasses, the test is collected in each subclass. Since the top-level Hypothesis test never changes, its inner test will be wrapped multiple times.

Double wrapping leads to execution errors caused by stale (closed) event loops in all test executions after the first.

This change adds an `original_test_function` attribute to the async function wrapper, in order to keep track of the original Hypothesis test. When re-wrapping would occur in subclasses pytest-asyncio wraps the original test function rather than the wrapper function.

Closes pytest-dev#231

Signed-off-by: Michael Seifert <m.seifert@digitalernachschub.de>
Tinche pushed a commit that referenced this issue Jan 7, 2022
Pytest-asyncio identifies Hypothesis test cases by their `is_hypothesis_test` flag. When setting up an async Hypothesis test pytest-asyncio replaces the function's `hypothesis.inner_test` attribute. The the top level function never changes.

When a Hypothesis test case is defined in a base class and inherited by subclasses, the test is collected in each subclass. Since the top-level Hypothesis test never changes, its inner test will be wrapped multiple times.

Double wrapping leads to execution errors caused by stale (closed) event loops in all test executions after the first.

This change adds an `original_test_function` attribute to the async function wrapper, in order to keep track of the original Hypothesis test. When re-wrapping would occur in subclasses pytest-asyncio wraps the original test function rather than the wrapper function.

Closes #231

Signed-off-by: Michael Seifert <m.seifert@digitalernachschub.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants