Skip to content

Commit 97f897f

Browse files
authored
Merge pull request #179 from frankie567/issue-178
Fix #178: handle spy on async functions
2 parents 2abec3b + f8f87fd commit 97f897f

File tree

7 files changed

+51
-5
lines changed

7 files changed

+51
-5
lines changed

.github/workflows/main.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,12 @@ jobs:
5656
5757
deploy:
5858

59+
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
60+
5961
runs-on: ubuntu-latest
6062

6163
needs: [build, linting]
6264

63-
6465
steps:
6566
- uses: actions/checkout@v1
6667
- name: Set up Python
@@ -75,7 +76,6 @@ jobs:
7576
run: |
7677
python setup.py sdist bdist_wheel
7778
- name: Publish package to PyPI
78-
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
7979
uses: pypa/gh-action-pypi-publish@master
8080
with:
8181
user: __token__

CHANGELOG.rst

+11
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
3.0.0 (UNRELEASED)
2+
------------------
3+
4+
* Python 2.7 and 3.4 are no longer supported. Users using ``pip 9`` or later will install
5+
a compatible version automatically.
6+
7+
* ``mocker.spy`` now also works with ``async def`` functions (`#179`_). Thanks `@frankie567`_ for the PR!
8+
9+
.. _#179: https://github.com/pytest-dev/pytest-mock/issues/179
10+
.. _@frankie567: https://github.com/frankie567
11+
112
2.0.0 (2020-01-04)
213
------------------
314

README.rst

+2
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ In addition, spy objects contain two extra attributes:
114114

115115
``mocker.spy`` also works for class and static methods.
116116

117+
As of version 3.0.0, ``mocker.spy`` also works with ``async def`` functions.
118+
117119
.. note::
118120

119121
In versions earlier than ``2.0``, the attributes were called ``return_value`` and

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
description="Thin-wrapper around the mock package for easier use with pytest",
2020
long_description=open("README.rst", encoding="utf-8").read(),
2121
keywords="pytest mock",
22-
extras_require={"dev": ["pre-commit", "tox"]},
22+
extras_require={"dev": ["pre-commit", "tox", "pytest-asyncio"]},
2323
classifiers=[
2424
"Development Status :: 5 - Production/Stable",
2525
"Framework :: Pytest",

src/pytest_mock/plugin.py

+19-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import asyncio
12
import functools
23
import inspect
34

@@ -96,7 +97,6 @@ def spy(self, obj, name):
9697
if isinstance(value, (classmethod, staticmethod)):
9798
autospec = False
9899

99-
@functools.wraps(method)
100100
def wrapper(*args, **kwargs):
101101
spy_obj.spy_return = None
102102
spy_obj.spy_exception = None
@@ -109,7 +109,24 @@ def wrapper(*args, **kwargs):
109109
spy_obj.spy_return = r
110110
return r
111111

112-
spy_obj = self.patch.object(obj, name, side_effect=wrapper, autospec=autospec)
112+
async def async_wrapper(*args, **kwargs):
113+
spy_obj.spy_return = None
114+
spy_obj.spy_exception = None
115+
try:
116+
r = await method(*args, **kwargs)
117+
except Exception as e:
118+
spy_obj.spy_exception = e
119+
raise
120+
else:
121+
spy_obj.spy_return = r
122+
return r
123+
124+
if asyncio.iscoroutinefunction(method):
125+
wrapped = functools.update_wrapper(async_wrapper, method)
126+
else:
127+
wrapped = functools.update_wrapper(wrapper, method)
128+
129+
spy_obj = self.patch.object(obj, name, side_effect=wrapped, autospec=autospec)
113130
spy_obj.spy_return = None
114131
spy_obj.spy_exception = None
115132
return spy_obj

tests/test_pytest_mock.py

+15
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,21 @@ def __call__(self, x):
405405
assert spy.spy_return == 20
406406

407407

408+
@pytest.mark.asyncio
409+
async def test_instance_async_method_spy(mocker):
410+
class Foo:
411+
async def bar(self, arg):
412+
return arg * 2
413+
414+
foo = Foo()
415+
spy = mocker.spy(foo, "bar")
416+
417+
result = await foo.bar(10)
418+
419+
spy.assert_called_once_with(10)
420+
assert result == 20
421+
422+
408423
@contextmanager
409424
def assert_traceback():
410425
"""

tox.ini

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ envlist = py{35,36,37,38}, linting, norewrite
55
passenv = USER USERNAME
66
deps =
77
coverage
8+
pytest-asyncio
89
commands =
910
coverage run --append --source={envsitepackagesdir}/pytest_mock -m pytest tests
1011

0 commit comments

Comments
 (0)