Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test Python 3.4 asyncio code?

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):

import asyncio import unittest  class TestSocketConnection(unittest.TestCase):     def setUp(self):         self.mock_server = MockServer("localhost", 1337)         self.socket_connection = SocketConnection("localhost", 1337)      @asyncio.coroutine     def test_sends_handshake_after_connect(self):         yield from self.socket_connection.connect()         self.assertTrue(self.mock_server.received_handshake()) 

When running this test case with the default test runner, the test will always succeed as the method executes only up until the first yield from instruction, after which it returns before executing any assertions. This causes tests to always succeed.

Is there a prebuilt test runner that is able to handle asynchronous code like this?

like image 238
Marvin Killing Avatar asked Apr 12 '14 17:04

Marvin Killing


People also ask

How do I test a function in Python?

First you need to create a test file. Then import the unittest module, define the testing class that inherits from unittest. TestCase, and lastly, write a series of methods to test all the cases of your function's behavior. First, you need to import a unittest and the function you want to test, formatted_name() .

How does Asyncio work Python?

Async IO takes long waiting periods in which functions would otherwise be blocking and allows other functions to run during that downtime. (A function that blocks effectively forbids others from running from the time that it starts until the time that it returns.)

How do you use assertRaises in Python?

There are two ways you can use assertRaises: using keyword arguments. Just pass the exception, the callable function and the parameters of the callable function as keyword arguments that will elicit the exception. Make a function call that should raise the exception with a context.


2 Answers

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) 
like image 166
Nico Rikken Avatar answered Oct 23 '22 08:10

Nico Rikken


I temporarily solved the problem using a decorator inspired by Tornado's gen_test:

def async_test(f):     def wrapper(*args, **kwargs):         coro = asyncio.coroutine(f)         future = coro(*args, **kwargs)         loop = asyncio.get_event_loop()         loop.run_until_complete(future)     return wrapper 

Like J.F. Sebastian suggested, this decorator will block until the test method coroutine has finished. This allows me to write test cases like this:

class TestSocketConnection(unittest.TestCase):     def setUp(self):         self.mock_server = MockServer("localhost", 1337)         self.socket_connection = SocketConnection("localhost", 1337)      @async_test     def test_sends_handshake_after_connect(self):         yield from self.socket_connection.connect()         self.assertTrue(self.mock_server.received_handshake()) 

This solution probably misses some edge cases.

I think a facility like this should added to Python's standard library to make asyncio and unittest interaction more convenient out of the box.

like image 34
Marvin Killing Avatar answered Oct 23 '22 06:10

Marvin Killing