Skip to content

Commit bd8ef60

Browse files
committed
fix: Fixed double wrapping of inherited Hypothesis tests.
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 <[email protected]>
1 parent 3d3063c commit bd8ef60

File tree

3 files changed

+32
-1
lines changed

3 files changed

+32
-1
lines changed

README.rst

+1
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ Changelog
175175
~~~~~~~~~~~~~~~~~~~
176176
- `pytest-asyncio` no longer alters existing event loop policies. `#168 <https://github.com/pytest-dev/pytest-asyncio/issues/168>`_, `#188 <https://github.com/pytest-dev/pytest-asyncio/issues/168>`_
177177
- Drop support for Python 3.6
178+
- Fixed an issue that prevented inherited asynchronous Hypothesis tests from working. `#231 <https://github.com/pytest-dev/pytest-asyncio/issues/231>`_
178179

179180
0.16.0 (2021-10-16)
180181
~~~~~~~~~~~~~~~~~~~

pytest_asyncio/plugin.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,16 @@ def pytest_pyfunc_call(pyfuncitem):
158158
"""
159159
if "asyncio" in pyfuncitem.keywords:
160160
if getattr(pyfuncitem.obj, "is_hypothesis_test", False):
161-
pyfuncitem.obj.hypothesis.inner_test = wrap_in_sync(
161+
hypothesis_inner_test = getattr(
162162
pyfuncitem.obj.hypothesis.inner_test,
163+
"original_test_function",
164+
pyfuncitem.obj.hypothesis.inner_test,
165+
)
166+
synchronous_test_function = wrap_in_sync(
167+
hypothesis_inner_test,
163168
_loop=pyfuncitem.funcargs["event_loop"],
164169
)
170+
pyfuncitem.obj.hypothesis.inner_test = synchronous_test_function
165171
else:
166172
pyfuncitem.obj = wrap_in_sync(
167173
pyfuncitem.obj, _loop=pyfuncitem.funcargs["event_loop"]
@@ -188,6 +194,8 @@ def inner(**kwargs):
188194
task.exception()
189195
raise
190196

197+
inner.original_test_function = func
198+
191199
return inner
192200

193201

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import hypothesis.strategies as st
2+
from hypothesis import given
3+
import pytest
4+
5+
6+
class BaseClass:
7+
@pytest.mark.asyncio
8+
@given(value=st.integers())
9+
async def test_hypothesis(self, value: int) -> None:
10+
assert True
11+
12+
13+
class TestOne(BaseClass):
14+
"""During the first execution the Hypothesis test is wrapped in a synchronous function."""
15+
16+
pass
17+
18+
19+
class TestTwo(BaseClass):
20+
"""Execute the test a second time to ensure that the test receives a fresh event loop."""
21+
22+
pass

0 commit comments

Comments
 (0)