How do I mock async call from one native coroutine to other one using unittest.mock.patch?
I currently have quite an awkward solution:
class CoroutineMock(MagicMock):
def __await__(self, *args, **kwargs):
future = Future()
future.set_result(self)
result = yield from future
return result
Then
class TestCoroutines(TestCase):
#patch('some.path', new_callable=CoroutineMock)
def test(self, mock):
some_action()
mock.assert_called_with(1,2,3)
This works but looks ugly. Is there more pythonic way to do this?
The solution was actually quite simple:
I just needed to convert __call__ method of mock into coroutine:
class AsyncMock(MagicMock):
async def __call__(self, *args, **kwargs):
return super(AsyncMock, self).__call__(*args, **kwargs)
This works perfectly, when mock is called, code receives native coroutine
Example usage:
#mock.patch('my.path.asyncio.sleep', new_callable=AsyncMock)
def test_stuff(sleep):
# code
Everyone's missing what's probably the simplest and clearest solution:
#patch('some.path')
def test(self, mock):
f = asyncio.Future()
f.set_result('whatever result you want')
process_smtp_message.return_value = f
mock.assert_called_with(1, 2, 3)
remember a coroutine can be thought of as just a function which is guaranteed to return a future which can, in turn be awaited.
Based on #scolvin answer I created this (imo) cleaner way:
import asyncio
def async_return(result):
f = asyncio.Future()
f.set_result(result)
return f
That's it, just use it around whatever return you want to be async, as in
mock = MagicMock(return_value=async_return("Example return"))
await mock()
Subclassing MagicMock will propagate your custom class for all the mocks generated from your coroutine mock. For instance, AsyncMock().__str__ will also become an AsyncMock which is probably not what you're looking for.
Instead, you might want to define a factory that creates a Mock (or a MagicMock) with custom arguments, for instance side_effect=coroutine(coro). Also, it might be a good idea to separate the coroutine function from the coroutine (as explained in the documentation).
Here is what I came up with:
from asyncio import coroutine
def CoroMock():
coro = Mock(name="CoroutineResult")
corofunc = Mock(name="CoroutineFunction", side_effect=coroutine(coro))
corofunc.coro = coro
return corofunc
An explanation of the different objects:
corofunc: the coroutine function mock
corofunc.side_effect(): the coroutine, generated for each call
corofunc.coro: the mock used by the coroutine to get the result
corofunc.coro.return_value: the value returned by the coroutine
corofunc.coro.side_effect: might be used to raise an exception
Example:
async def coro(a, b):
return await sleep(1, result=a+b)
def some_action(a, b):
return get_event_loop().run_until_complete(coro(a, b))
#patch('__main__.coro', new_callable=CoroMock)
def test(corofunc):
a, b, c = 1, 2, 3
corofunc.coro.return_value = c
result = some_action(a, b)
corofunc.assert_called_with(a, b)
assert result == c
Another way of mocking coroutine is to make coroutine, that returns mock. This way you can mock coroutines that will be passed into asyncio.wait or asyncio.wait_for.
This makes more universal coroutines though makes setup of tests more cumbersome:
def make_coroutine(mock)
async def coroutine(*args, **kwargs):
return mock(*args, **kwargs)
return coroutine
class Test(TestCase):
def setUp(self):
self.coroutine_mock = Mock()
self.patcher = patch('some.coroutine',
new=make_coroutine(self.coroutine_mock))
self.patcher.start()
def tearDown(self):
self.patcher.stop()
One more variant of "simplest" solution to mock a async object, which is just a one liner.
In source:
class Yo:
async def foo(self):
await self.bar()
async def bar(self):
# Some code
In test:
from asyncio import coroutine
yo = Yo()
# Here bounded method bar is mocked and will return a customised result.
yo.bar = Mock(side_effect=coroutine(lambda:'the awaitable should return this'))
event_loop.run_until_complete(yo.foo())
I don't know why nobody mentioned the default option available.
python provides a Async version of MagicMock.
You can read more about this here.
https://docs.python.org/3/library/unittest.mock.html#unittest.mock.AsyncMock
In case you are using patch then also you don't need to make any other change. It will automatically replace it with a async mock function if required.
Read more here
https://docs.python.org/3/library/unittest.mock.html#patch
You can set the return_value of an async method like so:
mock = unittest.mock.MagicMock()
mock.your_async_method.return_value = task_from_result(your_return_value)
async def task_from_result(result):
return result
The caller will have to do await your_async_method(..) just like as if the method wasn't mocked.
I like this approach, which also makes AsyncMock to behave exactly like Mock:
class AsyncMock:
def __init__(self, *args, **kwargs):
self.mock = Mock(*args, **kwargs)
async def __call__(self, *args, **kwargs):
return self.mock(*args, **kwargs)
def __getattr__(self, item):
return getattr(self.mock, item)
Then you can work with it in the same way like with Mock, i.e:
#pytest.mark.asyncio
async def test_async_mock_example(monkeypatch):
fn = AsyncMock(side_effect=ValueError)
with pytest.raises(ValueError):
await fn()
assert fn.call_count == 1
Related
Let's say I have this
class Test():
def __init__(self, number):
self.number = number
await self.TestPrint()
async def TestPrint(self):
print(self.number)
As you can see this won't work since __init__ is not async and I cannot call await for the function
I want to be able to run TestPrint within __init__ assuming I want to maintain this function async
I also want this to have nothing to do with anything else other than the class (other function, other classes, main, etc.)
Thank you for your time.
Like chepner mentioned in the comments:
An asynchronous class method that creates the object and then calls the TestPrint method before returning it sounds more appropriate.
This is the preferred way above all else and why a lot of there are a lot of functions that initialize internal asyncio classes rather than you directly instantiating them.
That said, if you wish to have this close to the class you can use a #classmethod which can be async. Your code would look like this:
class Test():
def __init__(self, number):
self.number = number
async def TestPrint(self):
print(self.number)
#classmethod
async def with_print(cls, number):
self = cls(number)
await self.TestPrint()
return self
async def main():
t = await Test.with_print(123)
# 't' is now your Test instance.
...
How do I mock async call from one native coroutine to other one using unittest.mock.patch?
I currently have quite an awkward solution:
class CoroutineMock(MagicMock):
def __await__(self, *args, **kwargs):
future = Future()
future.set_result(self)
result = yield from future
return result
Then
class TestCoroutines(TestCase):
#patch('some.path', new_callable=CoroutineMock)
def test(self, mock):
some_action()
mock.assert_called_with(1,2,3)
This works but looks ugly. Is there more pythonic way to do this?
The solution was actually quite simple:
I just needed to convert __call__ method of mock into coroutine:
class AsyncMock(MagicMock):
async def __call__(self, *args, **kwargs):
return super(AsyncMock, self).__call__(*args, **kwargs)
This works perfectly, when mock is called, code receives native coroutine
Example usage:
#mock.patch('my.path.asyncio.sleep', new_callable=AsyncMock)
def test_stuff(sleep):
# code
Everyone's missing what's probably the simplest and clearest solution:
#patch('some.path')
def test(self, mock):
f = asyncio.Future()
f.set_result('whatever result you want')
process_smtp_message.return_value = f
mock.assert_called_with(1, 2, 3)
remember a coroutine can be thought of as just a function which is guaranteed to return a future which can, in turn be awaited.
Based on #scolvin answer I created this (imo) cleaner way:
import asyncio
def async_return(result):
f = asyncio.Future()
f.set_result(result)
return f
That's it, just use it around whatever return you want to be async, as in
mock = MagicMock(return_value=async_return("Example return"))
await mock()
Subclassing MagicMock will propagate your custom class for all the mocks generated from your coroutine mock. For instance, AsyncMock().__str__ will also become an AsyncMock which is probably not what you're looking for.
Instead, you might want to define a factory that creates a Mock (or a MagicMock) with custom arguments, for instance side_effect=coroutine(coro). Also, it might be a good idea to separate the coroutine function from the coroutine (as explained in the documentation).
Here is what I came up with:
from asyncio import coroutine
def CoroMock():
coro = Mock(name="CoroutineResult")
corofunc = Mock(name="CoroutineFunction", side_effect=coroutine(coro))
corofunc.coro = coro
return corofunc
An explanation of the different objects:
corofunc: the coroutine function mock
corofunc.side_effect(): the coroutine, generated for each call
corofunc.coro: the mock used by the coroutine to get the result
corofunc.coro.return_value: the value returned by the coroutine
corofunc.coro.side_effect: might be used to raise an exception
Example:
async def coro(a, b):
return await sleep(1, result=a+b)
def some_action(a, b):
return get_event_loop().run_until_complete(coro(a, b))
#patch('__main__.coro', new_callable=CoroMock)
def test(corofunc):
a, b, c = 1, 2, 3
corofunc.coro.return_value = c
result = some_action(a, b)
corofunc.assert_called_with(a, b)
assert result == c
Another way of mocking coroutine is to make coroutine, that returns mock. This way you can mock coroutines that will be passed into asyncio.wait or asyncio.wait_for.
This makes more universal coroutines though makes setup of tests more cumbersome:
def make_coroutine(mock)
async def coroutine(*args, **kwargs):
return mock(*args, **kwargs)
return coroutine
class Test(TestCase):
def setUp(self):
self.coroutine_mock = Mock()
self.patcher = patch('some.coroutine',
new=make_coroutine(self.coroutine_mock))
self.patcher.start()
def tearDown(self):
self.patcher.stop()
One more variant of "simplest" solution to mock a async object, which is just a one liner.
In source:
class Yo:
async def foo(self):
await self.bar()
async def bar(self):
# Some code
In test:
from asyncio import coroutine
yo = Yo()
# Here bounded method bar is mocked and will return a customised result.
yo.bar = Mock(side_effect=coroutine(lambda:'the awaitable should return this'))
event_loop.run_until_complete(yo.foo())
I don't know why nobody mentioned the default option available.
python provides a Async version of MagicMock.
You can read more about this here.
https://docs.python.org/3/library/unittest.mock.html#unittest.mock.AsyncMock
In case you are using patch then also you don't need to make any other change. It will automatically replace it with a async mock function if required.
Read more here
https://docs.python.org/3/library/unittest.mock.html#patch
You can set the return_value of an async method like so:
mock = unittest.mock.MagicMock()
mock.your_async_method.return_value = task_from_result(your_return_value)
async def task_from_result(result):
return result
The caller will have to do await your_async_method(..) just like as if the method wasn't mocked.
I like this approach, which also makes AsyncMock to behave exactly like Mock:
class AsyncMock:
def __init__(self, *args, **kwargs):
self.mock = Mock(*args, **kwargs)
async def __call__(self, *args, **kwargs):
return self.mock(*args, **kwargs)
def __getattr__(self, item):
return getattr(self.mock, item)
Then you can work with it in the same way like with Mock, i.e:
#pytest.mark.asyncio
async def test_async_mock_example(monkeypatch):
fn = AsyncMock(side_effect=ValueError)
with pytest.raises(ValueError):
await fn()
assert fn.call_count == 1
I writing unit tests and for testing I want to mock a method of already existing object.
But looks like with asyncio corutines it is not so simple as it looks.
I tried to use MagickMock but it just doesn't work. There is no errors or exceptions, but with debugger I can see that f() is never being called.
My tests and object I want to patch look like this:
from unittest.mock import patch, MagicMock
class Service(object):
async def callback_handler(self, msg):
pass
async def handle(self, msg):
await self.callback_handler(msg)
class TestCase(object):
def setUp(self):
self.service = Service()
#patch('module.msg')
def test_my_case(self, msg_mock):
f_was_called = False
async def f():
global f_was_called
f_was_called = True
self.service.callback_handler = MagicMock(wraps=f) # here I try to mock
await self.service.handle(msg_mock)
assert f_was_called is True
How can I patch already instantiated object method with some custom one? Is there some issues with corutines?
Try using a context manager by replacing this line:
self.service.callback_handler = MagicMock(wraps=f) # here I try to mock
With this:
with mock.patch.object(self.service, 'callback_handler', side_effect=f) as mock_cb:
... # rest of code indented
I also faced the problem trying to mock asyncio and want into a lot of troubles,
I ended using pytest.asycio plugin .
#pytest.mark.asyncio
async def test_some_asyncio_code():
res = await library.do_something()
assert b'expected result' == res
In class Wizard, I would like to set attribute wand to the value returned by coroutine magic.
class Wizard:
async def acquire_wand(self):
self.wand = await magic()
This code is considered "bad Python", however, because wand is not defined in __init__. I cannot define it in __init__, though, because await may only be used in asynchronous functions.
class Wizard:
def __init__(self):
self.wand = None
async def acquire_wand(self):
self.wand = await magic()
async def perform_spell(self):
if self.wand is None:
await self.acquire_wand()
self.wand.wave()
I could set wand to None in __init__ and use if self.wand is None: wherever it is accessed, but this seems messy and unwieldy.
How can I ensure that wand is defined throughout the class?
Technically there is a trick with overriding __new__ method:
class InitCoroMixin:
""" Mixin for create initialization coroutine
"""
def __new__(cls, *args, **kwargs):
""" This is magic!
"""
instance = super().__new__(cls)
#asyncio.coroutine
def coro():
instance.__init__(*args, **kwargs)
yield from instance.__ainit__()
return instance
return coro()
#asyncio.coroutine
def __ainit__(self):
raise NotImplementedError
see aiohttp_traversal code for full example.
But I highly discourage the method: having I/O in constructor is usually a bad idea, please think about it.
Wrap your functions that need self.wand inside a decorator, that will yield a clean and workable solution:
def with_wand(fn):
def wrapper(self):
if not self.wand:
await self.acquire_wand()
fn(self)
return wrapper
#with_wand
async def perform_spell(self):
self.wand.wave()
Haven't tested the code, let us know if it works!
It seems that using the following is the best way of going about this problem.
class Wizard:
def __init__(self):
self.wand = None
async def learn(self):
self.wand = await magic()
async def perform_spell(self):
if self.wand is None:
raise Exception("You must first learn to use a wand!")
self.wand.wave()
I think you got your advice, but I would like to question your premise. Who told you it's "considered bad Python"? I give my objects attributes all the time, whenever I need them to remember something. They have __dict__s for a reason. Python is not Java.
How do I mock async call from one native coroutine to other one using unittest.mock.patch?
I currently have quite an awkward solution:
class CoroutineMock(MagicMock):
def __await__(self, *args, **kwargs):
future = Future()
future.set_result(self)
result = yield from future
return result
Then
class TestCoroutines(TestCase):
#patch('some.path', new_callable=CoroutineMock)
def test(self, mock):
some_action()
mock.assert_called_with(1,2,3)
This works but looks ugly. Is there more pythonic way to do this?
The solution was actually quite simple:
I just needed to convert __call__ method of mock into coroutine:
class AsyncMock(MagicMock):
async def __call__(self, *args, **kwargs):
return super(AsyncMock, self).__call__(*args, **kwargs)
This works perfectly, when mock is called, code receives native coroutine
Example usage:
#mock.patch('my.path.asyncio.sleep', new_callable=AsyncMock)
def test_stuff(sleep):
# code
Everyone's missing what's probably the simplest and clearest solution:
#patch('some.path')
def test(self, mock):
f = asyncio.Future()
f.set_result('whatever result you want')
process_smtp_message.return_value = f
mock.assert_called_with(1, 2, 3)
remember a coroutine can be thought of as just a function which is guaranteed to return a future which can, in turn be awaited.
Based on #scolvin answer I created this (imo) cleaner way:
import asyncio
def async_return(result):
f = asyncio.Future()
f.set_result(result)
return f
That's it, just use it around whatever return you want to be async, as in
mock = MagicMock(return_value=async_return("Example return"))
await mock()
Subclassing MagicMock will propagate your custom class for all the mocks generated from your coroutine mock. For instance, AsyncMock().__str__ will also become an AsyncMock which is probably not what you're looking for.
Instead, you might want to define a factory that creates a Mock (or a MagicMock) with custom arguments, for instance side_effect=coroutine(coro). Also, it might be a good idea to separate the coroutine function from the coroutine (as explained in the documentation).
Here is what I came up with:
from asyncio import coroutine
def CoroMock():
coro = Mock(name="CoroutineResult")
corofunc = Mock(name="CoroutineFunction", side_effect=coroutine(coro))
corofunc.coro = coro
return corofunc
An explanation of the different objects:
corofunc: the coroutine function mock
corofunc.side_effect(): the coroutine, generated for each call
corofunc.coro: the mock used by the coroutine to get the result
corofunc.coro.return_value: the value returned by the coroutine
corofunc.coro.side_effect: might be used to raise an exception
Example:
async def coro(a, b):
return await sleep(1, result=a+b)
def some_action(a, b):
return get_event_loop().run_until_complete(coro(a, b))
#patch('__main__.coro', new_callable=CoroMock)
def test(corofunc):
a, b, c = 1, 2, 3
corofunc.coro.return_value = c
result = some_action(a, b)
corofunc.assert_called_with(a, b)
assert result == c
Another way of mocking coroutine is to make coroutine, that returns mock. This way you can mock coroutines that will be passed into asyncio.wait or asyncio.wait_for.
This makes more universal coroutines though makes setup of tests more cumbersome:
def make_coroutine(mock)
async def coroutine(*args, **kwargs):
return mock(*args, **kwargs)
return coroutine
class Test(TestCase):
def setUp(self):
self.coroutine_mock = Mock()
self.patcher = patch('some.coroutine',
new=make_coroutine(self.coroutine_mock))
self.patcher.start()
def tearDown(self):
self.patcher.stop()
One more variant of "simplest" solution to mock a async object, which is just a one liner.
In source:
class Yo:
async def foo(self):
await self.bar()
async def bar(self):
# Some code
In test:
from asyncio import coroutine
yo = Yo()
# Here bounded method bar is mocked and will return a customised result.
yo.bar = Mock(side_effect=coroutine(lambda:'the awaitable should return this'))
event_loop.run_until_complete(yo.foo())
I don't know why nobody mentioned the default option available.
python provides a Async version of MagicMock.
You can read more about this here.
https://docs.python.org/3/library/unittest.mock.html#unittest.mock.AsyncMock
In case you are using patch then also you don't need to make any other change. It will automatically replace it with a async mock function if required.
Read more here
https://docs.python.org/3/library/unittest.mock.html#patch
You can set the return_value of an async method like so:
mock = unittest.mock.MagicMock()
mock.your_async_method.return_value = task_from_result(your_return_value)
async def task_from_result(result):
return result
The caller will have to do await your_async_method(..) just like as if the method wasn't mocked.
I like this approach, which also makes AsyncMock to behave exactly like Mock:
class AsyncMock:
def __init__(self, *args, **kwargs):
self.mock = Mock(*args, **kwargs)
async def __call__(self, *args, **kwargs):
return self.mock(*args, **kwargs)
def __getattr__(self, item):
return getattr(self.mock, item)
Then you can work with it in the same way like with Mock, i.e:
#pytest.mark.asyncio
async def test_async_mock_example(monkeypatch):
fn = AsyncMock(side_effect=ValueError)
with pytest.raises(ValueError):
await fn()
assert fn.call_count == 1