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

Async test function gets skipped with asyncio_mode = auto and @unittest.mock.patch.dict decorator #403

Open
nphilipp opened this issue Aug 29, 2022 · 9 comments
Labels
help wanted upstream The issue was reported to an upstream project

Comments

@nphilipp
Copy link

Hi!

In a PR for a project I'm working on, an async test method gets skipped with a PytestUnhandledCoroutineWarning even though pytest-asyncio is installed and asyncio_mode is auto. I poked at it for while and found that this is because the method was decorated with @unittest.mock.patch.dict(...). Here's a minimal reproducer which exposes the issue with Python 3.10.6, pytest-7.1.2, pytest-asyncio-0.19.0:

--- 8< --- test_the_things.py ---

from unittest import mock

import pytest

bar = {}


async def test_runs():
    pass


@mock.patch.dict("test_the_things.bar")
async def test_doesnt_run():
    pass


@pytest.mark.asyncio
@mock.patch.dict("test_the_things.bar")
async def test_runs_anyway():
    pass

--- >8 ---

Here's the output I get:

(pytest-asyncio-issue) nils@makake:~/test/python/pytest-asyncio> pytest --asyncio-mode=auto -v
======================================== test session starts =========================================
platform linux -- Python 3.10.6, pytest-7.1.2, pluggy-1.0.0 -- /home/nils/.virtualenvs/pytest-asyncio-issue/bin/python
cachedir: .pytest_cache
rootdir: /home/nils/test/python/pytest-asyncio
plugins: asyncio-0.19.0
asyncio: mode=auto
collected 3 items                                                                                    

test_the_things.py::test_runs PASSED                                                           [ 33%]
test_the_things.py::test_doesnt_run SKIPPED (async def function and no async plugin instal...) [ 66%]
test_the_things.py::test_runs_anyway PASSED                                                    [100%]

========================================== warnings summary ==========================================
test_the_things.py::test_doesnt_run
  /home/nils/.virtualenvs/pytest-asyncio-issue/lib/python3.10/site-packages/_pytest/python.py:181: PytestUnhandledCoroutineWarning: async def functions are not natively supported and have been skipped.
  You need to install a suitable plugin for your async framework, for example:
    - anyio
    - pytest-asyncio
    - pytest-tornasync
    - pytest-trio
    - pytest-twisted
    warnings.warn(PytestUnhandledCoroutineWarning(msg.format(nodeid)))

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
============================== 2 passed, 1 skipped, 1 warning in 0.01s ===============================
sys:1: RuntimeWarning: coroutine 'test_doesnt_run' was never awaited
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
(pytest-asyncio-issue) nils@makake:~/test/python/pytest-asyncio>

Either without the decorator or by explicitly adding @pytest.mark.asyncio the async test functions don't get skipped.

@sanders41
Copy link

This also happens when other marks are applied.

@pytest.mark.integration
async def test_runs():
    ...

The test gets skipped with the same warning listed above when running pytest -m integraton

As above applying the decorator fixes it:

@pytest.mark.integration
@pytest.mark.asyncio
async def test_runs():
    ...

@sanders41
Copy link

On closer inspection after looking at this issue I see our tests that are skipped are also marked with a patch @mock.patch("patched.function"). We found a few marked as @pytest.mark.integration that didn't get skipped so it seems as though it is specific to patching.

@seifertm
Copy link
Contributor

seifertm commented Sep 6, 2022

When I did a test a couple of days ago I could reproduce the issue with @mock.patch.dict, but not @mock.patch. I found that patch performs some special treatment for coroutines, which patch.dict does not.

In my opinion there are two things to be done here:

  1. Investigate if this is a CPython bug
  2. Find an intermediate solution for pytest-asyncio

@sanders41
Copy link

In our code base it has been hard for me to track down the exact cause because it has shown up in some places I didn't expect and not in some I did. I know we do have @mock.patch.dict in places so that could be what is happening.

I looked into it some with @mock.patch.dict and saw the function no longer gets reported as an async function with inspect which seems to match up with what you found.

@seifertm
Copy link
Contributor

seifertm commented Oct 8, 2022

I opened a CPython issue regarding this: python/cpython#98086

@stillborn333

This comment was marked as spam.

@seifertm seifertm added the upstream The issue was reported to an upstream project label Oct 21, 2022
@seifertm
Copy link
Contributor

seifertm commented Dec 1, 2022

The issue has been resolved for CPython 3.12 and the patch was backported to CPython 3.10 and CPython 3.11.

3.10.9 is scheduled for 2022-12-05 and 3.11.1 should be released in December as well.

CPython 3.7 is not affected by this bug, but the question is if we want to include a workaround for 3.8 and 3.9.

@seifertm
Copy link
Contributor

The issue continues to be valid. If anyone wants to resolve it for Python 3.8 or 3.9, they're welcome to provide a backport that can be included in pytest-asyncio.

@seifertm
Copy link
Contributor

Python 3.8 is no longer supported by recent versions of pytest-asyncio. A backport of this issue would still be accepted, since we support Python 3.9.

However, Python 3.9 reaches end-of-life in October 2025, at which point this issue can be closed anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted upstream The issue was reported to an upstream project
Projects
None yet
Development

No branches or pull requests

4 participants