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

Fix auto marking of async hypothesis tests in auto mode #259

Merged
merged 2 commits into from Jan 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.rst
Expand Up @@ -256,6 +256,10 @@ or an async framework such as `asynctest <https://asynctest.readthedocs.io/en/la

Changelog
---------
0.17.1 (UNRELEASED)
~~~~~~~~~~~~~~~~~~~
- Fixes a bug that prevents async Hypothesis tests from working without explicit ``asyncio`` marker when ``--asyncio-mode=auto`` is set. `#258 <https://github.com/pytest-dev/pytest-asyncio/issues/258>`_

0.17.0 (22-01-13)
~~~~~~~~~~~~~~~~~~~
- `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>`_
Expand Down
18 changes: 16 additions & 2 deletions pytest_asyncio/plugin.py
Expand Up @@ -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))
Expand All @@ -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"""

Expand Down Expand Up @@ -288,7 +298,7 @@ def pytest_pyfunc_call(pyfuncitem):
where the wrapped test coroutine is executed in an event loop.
"""
if "asyncio" in pyfuncitem.keywords:
if getattr(pyfuncitem.obj, "is_hypothesis_test", False):
if _is_hypothesis_test(pyfuncitem.obj):
pyfuncitem.obj.hypothesis.inner_test = wrap_in_sync(
pyfuncitem.obj.hypothesis.inner_test,
_loop=pyfuncitem.funcargs["event_loop"],
Expand All @@ -300,6 +310,10 @@ def pytest_pyfunc_call(pyfuncitem):
yield


def _is_hypothesis_test(function) -> bool:
return getattr(function, "is_hypothesis_test", False)


def wrap_in_sync(func, _loop):
"""Return a sync wrapper around an async function executing it in the
current event loop."""
Expand Down
46 changes: 46 additions & 0 deletions tests/hypothesis/test_base.py
Expand Up @@ -2,6 +2,7 @@
sync shim for Hypothesis.
"""
import asyncio
from textwrap import dedent

import pytest
from hypothesis import given, strategies as st
Expand Down Expand Up @@ -40,3 +41,48 @@ async def test_can_use_fixture_provided_event_loop(event_loop, n):
semaphore = asyncio.Semaphore(value=0)
event_loop.call_soon(semaphore.release)
await semaphore.acquire()


def test_async_auto_marked(pytester):
pytester.makepyfile(
dedent(
"""\
import asyncio
import pytest
from hypothesis import given
import hypothesis.strategies as st

pytest_plugins = 'pytest_asyncio'

@given(n=st.integers())
async def test_hypothesis(n: int):
assert isinstance(n, int)
"""
)
)
result = pytester.runpytest("--asyncio-mode=auto")
result.assert_outcomes(passed=1)


def test_sync_not_auto_marked(pytester):
"""Assert that synchronous Hypothesis functions are not marked with asyncio"""
pytester.makepyfile(
dedent(
"""\
import asyncio
import pytest
from hypothesis import given
import hypothesis.strategies as st

pytest_plugins = 'pytest_asyncio'

@given(n=st.integers())
def test_hypothesis(request, n: int):
markers = [marker.name for marker in request.node.own_markers]
assert "asyncio" not in markers
assert isinstance(n, int)
"""
)
)
result = pytester.runpytest("--asyncio-mode=auto")
result.assert_outcomes(passed=1)