Exception raised when patch a coroutine classmethod
Closed this issue · 6 comments
I try to patch coroutinu classmethod, but it raise an exception:
here the code:
# target.py
class Target(object):
@classmethod
async def a(cls):
return 123
async def b(self):
return 456
# test_target.py
import asynctest
from target import Target
class TargetTest(asynctest.TestCase):
@asynctest.patch('target.Target.a')
async def test_classmethod(self, mock):
print(mock) # it print a MagicMock object, not a CoroutineMock object
mock.return_value = 456
rt = await Target.a()
self.assertEqual(rt, 123)
@asynctest.patch('target.Target.b')
async def test_method(self, mock):
print(mock)
mock.return_value = 789
t = Target()
rt = await t.b()
self.assertEqual(rt, 789)
if __name__ == '__main__':
asynctest.main()
Here is the Exception:
root@xxx-VirtualBox:/code/# python3 test_target.py
<MagicMock name='a' id='140488891004632'>
E
======================================================================
ERROR: test_classmethod (__main__.TargetTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/local/lib/python3.6/dist-packages/asynctest/case.py", line 297, in run
self._run_test_method(testMethod)
File "/usr/local/lib/python3.6/dist-packages/asynctest/case.py", line 354, in _run_test_method
self.loop.run_until_complete(result)
File "/usr/local/lib/python3.6/dist-packages/asynctest/case.py", line 224, in wrapper
return method(*args, **kwargs)
File "/usr/lib/python3.6/asyncio/base_events.py", line 468, in run_until_complete
return future.result()
File "/usr/local/lib/python3.6/dist-packages/asynctest/_awaitable.py", line 21, in wrapper
return await coroutine(*args, **kwargs)
File "/usr/local/lib/python3.6/dist-packages/asynctest/mock.py", line 985, in __next__
return self.gen.send(None)
File "test_target.py", line 11, in test_classmethod
rt = await Target.a()
TypeError: object int can't be used in 'await' expression
----------------------------------------------------------------------
Ran 1 test in 0.004s
FAILED (errors=1)
Please tell me what's wrong?
With asynctest.patch
you need to patch a class, not a method. In your case useasynctest.patch.object(target.Target, 'a')
.
Which version of asynctest are you using? It exists in the current release:
$ virtualenv venv
$ . venv/bin/activate
$ pip install asynctest
$ python
Python 3.6.5 (default, May 11 2018, 04:00:52)
[GCC 8.1.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import asynctest
>>> asynctest.patch
<function patch at 0x7f3207505ae8>
>>> asynctest.patch.object
<function _patch_object at 0x7f32075241e0>
I updated the package version and there is indeed that function.
But the error still appears. And It seem happened only when patching a coroutine classmethod.
I also debugged my code. I guess the problem may be here:
# mock.py line 857
def _update_new_callable(patcher, new, new_callable):
if new == DEFAULT and not new_callable:
if asyncio.iscoroutinefunction(patcher.get_original()[0]):
patcher.new_callable = CoroutineMock
else:
patcher.new_callable = MagicMock
return patcher
When I patch a coroutine classmethod, The return value of function patcher.get_original()[0]
is not a coroutine function. But when I path a corutine instance method, It return the desired result. Hope this can helps.
I'm sorry to trouble you so much and forgive my pool English....
This is my test code:
import asynctest
from target import Target
class TargetTest(asynctest.TestCase):
@asynctest.patch.object(Target, 'a')
async def test_classmethod(self, mock):
print('test_classmethod: {}'.format(mock))
mock.return_value = 456
rt = await Target.a()
self.assertEqual(rt, 123)
@asynctest.patch.object(Target, 'b')
async def test_method(self, mock):
print('test_method: {}'.format(mock))
mock.return_value = 789
t = Target()
rt = await t.b()
self.assertEqual(rt, 789)
if __name__ == '__main__':
asynctest.main()
This is the trackback info:
root@xxx-VirtualBox:/code# python3 test_target.py
test_classmethod: <MagicMock name='a' id='140173711134280'> <--- It should be a CoroutineMock object
Etest_method: <CoroutineMock name='b' id='140173711259128'>
.
======================================================================
ERROR: test_classmethod (__main__.TargetTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/local/lib/python3.6/dist-packages/asynctest/case.py", line 297, in run
self._run_test_method(testMethod)
File "/usr/local/lib/python3.6/dist-packages/asynctest/case.py", line 354, in _run_test_method
self.loop.run_until_complete(result)
File "/usr/local/lib/python3.6/dist-packages/asynctest/case.py", line 224, in wrapper
return method(*args, **kwargs)
File "/usr/lib/python3.6/asyncio/base_events.py", line 468, in run_until_complete
return future.result()
File "/usr/local/lib/python3.6/dist-packages/asynctest/_awaitable.py", line 21, in wrapper
return await coroutine(*args, **kwargs)
File "/usr/local/lib/python3.6/dist-packages/asynctest/mock.py", line 985, in __next__
return self.gen.send(None)
File "test_target.py", line 11, in test_classmethod
rt = await Target.a()
TypeError: object int can't be used in 'await' expression
----------------------------------------------------------------------
Thanks for the detailed report. I'm looking at it.