From 06d1848a2dccd7b5e371ac72a0364697d28ec9a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Voron?= Date: Sat, 25 Jan 2020 09:33:57 +0100 Subject: [PATCH 1/3] Fix #178: handle spy on async functions --- setup.py | 2 +- src/pytest_mock/plugin.py | 21 +++++++++++++++++++-- tests/test_pytest_mock.py | 15 +++++++++++++++ tox.ini | 1 + 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index f54b68c..0ad6975 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ description="Thin-wrapper around the mock package for easier use with pytest", long_description=open("README.rst", encoding="utf-8").read(), keywords="pytest mock", - extras_require={"dev": ["pre-commit", "tox"]}, + extras_require={"dev": ["pre-commit", "tox", "pytest-asyncio"]}, classifiers=[ "Development Status :: 5 - Production/Stable", "Framework :: Pytest", diff --git a/src/pytest_mock/plugin.py b/src/pytest_mock/plugin.py index a39a5bf..cfde254 100644 --- a/src/pytest_mock/plugin.py +++ b/src/pytest_mock/plugin.py @@ -1,3 +1,4 @@ +import asyncio import functools import inspect @@ -96,7 +97,6 @@ def spy(self, obj, name): if isinstance(value, (classmethod, staticmethod)): autospec = False - @functools.wraps(method) def wrapper(*args, **kwargs): spy_obj.spy_return = None spy_obj.spy_exception = None @@ -109,7 +109,24 @@ def wrapper(*args, **kwargs): spy_obj.spy_return = r return r - spy_obj = self.patch.object(obj, name, side_effect=wrapper, autospec=autospec) + async def async_wrapper(*args, **kwargs): + spy_obj.spy_return = None + spy_obj.spy_exception = None + try: + r = await method(*args, **kwargs) + except Exception as e: + spy_obj.spy_exception = e + raise + else: + spy_obj.spy_return = r + return r + + if asyncio.iscoroutinefunction(method): + wrapped = functools.update_wrapper(async_wrapper, method) + else: + wrapped = functools.update_wrapper(wrapper, method) + + spy_obj = self.patch.object(obj, name, side_effect=wrapped, autospec=autospec) spy_obj.spy_return = None spy_obj.spy_exception = None return spy_obj diff --git a/tests/test_pytest_mock.py b/tests/test_pytest_mock.py index a8121ca..e7f6122 100644 --- a/tests/test_pytest_mock.py +++ b/tests/test_pytest_mock.py @@ -405,6 +405,21 @@ def __call__(self, x): assert spy.spy_return == 20 +@pytest.mark.asyncio +async def test_instance_async_method_spy(mocker): + class Foo: + async def bar(self, arg): + return arg * 2 + + foo = Foo() + spy = mocker.spy(foo, "bar") + + result = await foo.bar(10) + + spy.assert_called_once_with(10) + assert result == 20 + + @contextmanager def assert_traceback(): """ diff --git a/tox.ini b/tox.ini index 79fa92f..99c4e6c 100644 --- a/tox.ini +++ b/tox.ini @@ -5,6 +5,7 @@ envlist = py{35,36,37,38}, linting, norewrite passenv = USER USERNAME deps = coverage + pytest-asyncio commands = coverage run --append --source={envsitepackagesdir}/pytest_mock -m pytest tests From 3efdf9fa956aa32593e2694dac28ea7b69c6c528 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 28 Jan 2020 20:23:34 -0300 Subject: [PATCH 2/3] Add CHANGELOG and docs for #178 --- CHANGELOG.rst | 11 +++++++++++ README.rst | 2 ++ 2 files changed, 13 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 09b85c8..1311366 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,14 @@ +3.0.0 (UNRELEASED) +------------------ + +* Python 2.7 and 3.4 are no longer supported. Users using ``pip 9`` or later will install + a compatible version automatically. + +* ``mocker.spy`` now also works with ``async def`` functions (`#179`_). Thanks `@frankie567`_ for the PR! + +.. _#179: https://github.com/pytest-dev/pytest-mock/issues/179 +.. _@frankie567: https://github.com/frankie567 + 2.0.0 (2020-01-04) ------------------ diff --git a/README.rst b/README.rst index 6e9d364..dbee6ca 100644 --- a/README.rst +++ b/README.rst @@ -114,6 +114,8 @@ In addition, spy objects contain two extra attributes: ``mocker.spy`` also works for class and static methods. +As of version 3.0.0, ``mocker.spy`` also works with ``async def`` functions. + .. note:: In versions earlier than ``2.0``, the attributes were called ``return_value`` and From f8f87fd840eba67eed9f6c010c4e57b2bb6aced0 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 28 Jan 2020 20:29:37 -0300 Subject: [PATCH 3/3] Skip 'deploy' job unless on a tag Fix #158 --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 939c742..f5da49b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -56,11 +56,12 @@ jobs: deploy: + if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') + runs-on: ubuntu-latest needs: [build, linting] - steps: - uses: actions/checkout@v1 - name: Set up Python @@ -75,7 +76,6 @@ jobs: run: | python setup.py sdist bdist_wheel - name: Publish package to PyPI - if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') uses: pypa/gh-action-pypi-publish@master with: user: __token__