pythonpytestpython-asynciopytest-asyncio

Pytest two async functions both with endless loops and await commands


I am unit testing a python code. It has an asyncio loop. The main two functions in this loop have endless while loops. I have tested the non-async parts of the code. I am wondering what is the best strategy to unit test these two functions:

import asyncio

def main():
    loop = asyncio.get_event_loop()
    loop.run_until_complete(run_async_tasks())
    .
    .
    

async def run_async_tasks():
    # initialize loop and its variables
    loop = asyncio.get_event_loop()
    queue = asyncio.Queue(maxsize=100)

    # Creating Asyncio tasks
    task1 = loop.create_task(receive_data(queue), name="receive_data")
    task2 = loop.create_task(analyse_data(queue), name="analyse_data")

    await asyncio.gather(task1, task2)

async def receive_data(queue):
    # Data packets are yielded from an non-ending stream by sync_receive_data_packets
    for data_packet in sync_receive_data_packets():
        await queue.put(data_packet)

async def analyse_data(queue):
    while not termination_signal_received():
        data_packet = await queue.get()
        sync_data_analysis(data_packet)
        queue.task_done()

My question is if/how it is possible to unit test receive_data and analyse_data.

The main logic for reading the data (sync_receive_data_packets) and executing the analysis (sync_data_analysis) are in non-async functions for the sake of simplicity. They are successfully tested. I am not sure how to test the await queue.put and await queue.get() parts especially when they are in endless loops. In fact I am wondering if these functions can be unit tested as they don't return anything. One puts an item into a queue and the other reads it.


Solution

  • Both functions are not "endless" - the first will end when the generator returned by sync_receive_data_packets is exhausted, and the other whenever termination_signal_received() returns True. Just use mock.patch to point these two functions to callables under the control of your tests. Your tests should further verify if the queue is filled for the first function, and if it is consumed and sync_data_analisys is called with the proper values (it should be patched as well)

    Otherwise (if they were being repeated with a while True, with no stopping conditions), the way to unit test these would be to write a specialized Queue class that would work as a queue, but that could raise an exception under control of the test code when .get or .put are called.