From bd8f210f24de1f919c37d76d0a6da717a183ebd2 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Thu, 13 Jan 2022 21:40:01 +0100 Subject: [PATCH] fix: Fixes a bug that prevents async Hypothesis tests from working without explicit "asyncio" marker when "--asyncio-mode=auto" is set. The option --asyncio-mode=auto marks all async functions with the asyncio mark during the collection phase. However, when pytest collects the Hypothesis test, the @given decorator has already been applied and the Hypothesis test function is no longer a coroutine. This commit extends the "pytest_pycollect_makeitem" hook to mark Hypothesis tests whose function body is a coroutine. Closes #258 Signed-off-by: Michael Seifert --- README.rst | 4 ++++ pytest_asyncio/plugin.py | 12 +++++++++++- tests/hypothesis/test_base.py | 17 +++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index c63f72d2..9d2257a5 100644 --- a/README.rst +++ b/README.rst @@ -256,6 +256,10 @@ or an async framework such as `asynctest `_ + 0.17.0 (22-01-13) ~~~~~~~~~~~~~~~~~~~ - `pytest-asyncio` no longer alters existing event loop policies. `#168 `_, `#188 `_ diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 4f5a0591..04b5e139 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -115,7 +115,13 @@ def pytest_configure(config): @pytest.mark.tryfirst def pytest_pycollect_makeitem(collector, name, obj): """A pytest hook to collect asyncio coroutines.""" - if collector.funcnamefilter(name) and _is_coroutine(obj): + if not collector.funcnamefilter(name): + return + if ( + _is_coroutine(obj) + or _is_hypothesis_test(obj) + and _hypothesis_test_wraps_coroutine(obj) + ): item = pytest.Function.from_parent(collector, name=name) if "asyncio" in item.keywords: return list(collector._genfunctions(name, obj)) @@ -128,6 +134,10 @@ def pytest_pycollect_makeitem(collector, name, obj): return ret +def _hypothesis_test_wraps_coroutine(function): + return _is_coroutine(function.hypothesis.inner_test) + + class FixtureStripper: """Include additional Fixture, and then strip them""" diff --git a/tests/hypothesis/test_base.py b/tests/hypothesis/test_base.py index e9273d0e..9ba335d7 100644 --- a/tests/hypothesis/test_base.py +++ b/tests/hypothesis/test_base.py @@ -6,6 +6,8 @@ import pytest from hypothesis import given, strategies as st +from pytest_asyncio.plugin import Mode + @pytest.fixture(scope="module") def event_loop(): @@ -26,6 +28,21 @@ async def test_mark_outer(n): assert isinstance(n, int) +@given(n=st.integers()) +async def test_async_auto_marked(pytestconfig, n: int): + assert pytestconfig.inicfg["asyncio_mode"] == Mode.AUTO + assert isinstance(n, int) + + +@given(n=st.integers()) +def test_sync_not_auto_marked(pytestconfig, request, n: int): + """Assert that synchronous Hypothesis functions are not marked with asyncio""" + assert pytestconfig.inicfg["asyncio_mode"] == Mode.AUTO + markers = [marker.name for marker in request.node.own_markers] + assert "asyncio" not in markers + assert isinstance(n, int) + + @pytest.mark.parametrize("y", [1, 2]) @given(x=st.none()) @pytest.mark.asyncio