When I was trying to mock an async function in unittest with MagicMock, I got this exception:
TypeError: object MagicMock can't be used in 'await' expression
With sample code like:
# source code
class Service:
async def compute(self, x):
return x
class App:
def __init__(self):
self.service = Service()
async def handle(self, x):
return await self.service.compute(x)
# test code
import asyncio
import unittest
from unittest.mock import patch
class TestApp(unittest.TestCase):
@patch('__main__.Service')
def test_handle(self, mock):
loop = asyncio.get_event_loop()
app = App()
res = loop.run_until_complete(app.handle('foo'))
app.service.compute.assert_called_with("foo")
if __name__ == '__main__':
unittest.main()
How should I fix it with built-in python3 libraries?
I ended up with this hack.
# monkey patch MagicMock
async def async_magic():
pass
MagicMock.__await__ = lambda x: async_magic().__await__()
It only works for MagicMock, not other pre-defined return_value
You can get mocks to return objects that can be awaited by using a Future. The following is a pytest test case, but something similar should be possible with unittest.
async def test_that_mock_can_be_awaited():
mock = MagicMock(return_value=Future())
mock.return_value.set_result(123)
result = await mock()
assert result == 123
In your case, since you're patching Service
(which gets passed in as mock
), mock.return_value = Future()
should do the trick.
In python 3.8+ you can make use of the AsyncMock
async def test_that_mock_can_be_awaited():
mock = AsyncMock()
mock.x.return_value = 123
result = await mock.x()
assert result == 123
The class AsyncMock
object will behave so the object is recognized as an async function, and the result of a call is an awaitable.
>>> mock = AsyncMock()
>>> asyncio.iscoroutinefunction(mock)
True
>>> inspect.isawaitable(mock())
True
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With