What\'s the best way to write unit tests for code using the Python 3.4 asyncio
library? Assume I want to test a TCP client (SocketConnection
):
pytest-asyncio looks promising:
@pytest.mark.asyncio
async def test_some_asyncio_code():
res = await library.do_something()
assert b'expected result' == res
You can also use aiounittest that takes similar approach as @Andrew Svetlov, @Marvin Killing answers and wrap it in easy to use AsyncTestCase
class:
import asyncio
import aiounittest
async def add(x, y):
await asyncio.sleep(0.1)
return x + y
class MyTest(aiounittest.AsyncTestCase):
async def test_async_add(self):
ret = await add(5, 6)
self.assertEqual(ret, 11)
# or 3.4 way
@asyncio.coroutine
def test_sleep(self):
ret = yield from add(5, 6)
self.assertEqual(ret, 11)
# some regular test code
def test_something(self):
self.assertTrue(true)
As you can see the async case is handled by AsyncTestCase
. It supports also synchronous test. There is a possibility to provide custom event loop, just override AsyncTestCase.get_event_loop
.
If you prefer (for some reason) the other TestCase class (eg unittest.TestCase
), you might use async_test
decorator:
import asyncio
import unittest
from aiounittest import async_test
async def add(x, y):
await asyncio.sleep(0.1)
return x + y
class MyTest(unittest.TestCase):
@async_test
async def test_async_add(self):
ret = await add(5, 6)
self.assertEqual(ret, 11)
Since Python 3.8 unittest comes with the IsolatedAsyncioTestCase function, designed for this purpose.
from unittest import IsolatedAsyncioTestCase
class Test(IsolatedAsyncioTestCase):
async def test_functionality(self):
result = await functionality()
self.assertEqual(expected, result)
I usually define my async tests as coroutines and use a decorator for "syncing" them:
import asyncio
import unittest
def sync(coro):
def wrapper(*args, **kwargs):
loop = asyncio.get_event_loop()
loop.run_until_complete(coro(*args, **kwargs))
return wrapper
class TestSocketConnection(unittest.TestCase):
def setUp(self):
self.mock_server = MockServer("localhost", 1337)
self.socket_connection = SocketConnection("localhost", 1337)
@sync
async def test_sends_handshake_after_connect(self):
await self.socket_connection.connect()
self.assertTrue(self.mock_server.received_handshake())