The following code fails with TypeError: 'Mock' object is not iterable
in ImBeingTested.i_call_other_coroutines
because I've replaced ImGoingToBeMocked
by a Mock object.
How can I mock coroutines?
class ImGoingToBeMocked: @asyncio.coroutine def yeah_im_not_going_to_run(self): yield from asyncio.sleep(1) return "sup" class ImBeingTested: def __init__(self, hidude): self.hidude = hidude @asyncio.coroutine def i_call_other_coroutines(self): return (yield from self.hidude.yeah_im_not_going_to_run()) class TestImBeingTested(unittest.TestCase): def test_i_call_other_coroutines(self): mocked = Mock(ImGoingToBeMocked) ibt = ImBeingTested(mocked) ret = asyncio.get_event_loop().run_until_complete(ibt.i_call_other_coroutines())
data = iter([b'data', b'']) @asyncio. coroutine def read(*args): return next(data) mocked. read = Mock(wraps=read) # Here, the business class would use its . read() method which # would first read 4 bytes of data, and then no data # on its second read.
The method create_task takes a coroutine object as a parameter and returns a Task object, which inherits from asyncio. Future . The call creates the task inside the event loop for the current thread, and starts the task executing at the beginning of the coroutine's code-block.
October 10, 2019 October 10, 2019 Sebastian asyncio. tl;dr ensure_future let's us execute a coroutine in the background, without explicitly waiting for it to finish. If we need, we can wait for it later or poll for result. In other words, this is a way of executing code in asyncio without await.
Since mock
library doesn't support coroutines I create mocked coroutines manually and assign those to mock object. A bit more verbose but it works.
Your example may look like this:
import asyncio import unittest from unittest.mock import Mock class ImGoingToBeMocked: @asyncio.coroutine def yeah_im_not_going_to_run(self): yield from asyncio.sleep(1) return "sup" class ImBeingTested: def __init__(self, hidude): self.hidude = hidude @asyncio.coroutine def i_call_other_coroutines(self): return (yield from self.hidude.yeah_im_not_going_to_run()) class TestImBeingTested(unittest.TestCase): def test_i_call_other_coroutines(self): mocked = Mock(ImGoingToBeMocked) ibt = ImBeingTested(mocked) @asyncio.coroutine def mock_coro(): return "sup" mocked.yeah_im_not_going_to_run = mock_coro ret = asyncio.get_event_loop().run_until_complete( ibt.i_call_other_coroutines()) self.assertEqual("sup", ret) if __name__ == '__main__': unittest.main()
I am writting a wrapper to unittest which aims at cutting the boilerplate when writting tests for asyncio.
The code lives here: https://github.com/Martiusweb/asynctest
You can mock a coroutine with asynctest.CoroutineMock
:
>>> mock = CoroutineMock(return_value='a result') >>> asyncio.iscoroutinefunction(mock) True >>> asyncio.iscoroutine(mock()) True >>> asyncio.run_until_complete(mock()) 'a result'
It also works with the side_effect
attribute, and an asynctest.Mock
with a spec
can return CoroutineMock:
>>> asyncio.iscoroutinefunction(Foo().coroutine) True >>> asyncio.iscoroutinefunction(Foo().function) False >>> asynctest.Mock(spec=Foo()).coroutine <class 'asynctest.mock.CoroutineMock'> >>> asynctest.Mock(spec=Foo()).function <class 'asynctest.mock.Mock'>
All the features of unittest.Mock are expected to work correctly (patch(), etc).
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