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\' expr
I found this comment very useful when trying to await
a mock object in Python < 3.8. You simply create a child class AsyncMock
that inherits from MagicMock
and overwrites a __call__
method to be a coroutine:
class AsyncMock(MagicMock):
async def __call__(self, *args, **kwargs):
return super(AsyncMock, self).__call__(*args, **kwargs)
Then, inside your test, do:
@pytest.mark.asyncio
async def test_my_method():
# Test "my_method" coroutine by injecting an async mock
my_mock = AsyncMock()
assert await my_method(my_mock)
you might also want to install pytest-asyncio
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
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.
shaun shia provided really good universal solution, but i found what in python 3.8 you can use just @patch('__main__.Service', new=AsyncMock)