You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在pytest中获取被测asyncio函数事件循环的所有任务?

问题:如何在pytest-asyncio测试中获取被测函数的事件循环任务

我有如下fetcher.py模块代码,需要用pytest结合pytest-asyncio插件测试。尝试在tests.py中获取被测函数事件循环的所有任务,但断言失败。

测试代码(tests.py)

import asyncio
import pytest
from fetcher import main

@pytest.mark.asyncio
async def test_01(self):
    await main(10)
    all_tasks = len(asyncio.all_tasks()) # 获取任务数量
    assert all_tasks == 10

断言错误

1 != 10

Expected :10
Actual   :1

我推测这是因为获取的是pytest事件循环的任务而非main(10)的事件循环任务,当前仅存在1个任务。

被测代码(fetcher.py)

import asyncio


async def worker(queue: asyncio.Queue):
    while True:
        current_url = await queue.get()
        try:
            if current_url is None:
                break
            print(f"{current_url}: OK")
            print(len(asyncio.all_tasks())) # 输出10
        finally:
            queue.task_done()


async def main(max_requests):
    queue = asyncio.Queue(maxsize=max_requests)
    workers = {asyncio.create_task(worker(queue)) for _ in range(max_requests)}
    urls = ["https://example1.org/", "https://example2.org/", "https://example3.org/", "https://example4.org/"]
    for url in urls:
        await queue.put(url)
    for _ in range(len(workers)):
        await queue.put(None)
    await queue.join()

if __name__ == "__main__":
    asyncio.run(main(10))

解决方案

问题核心是:你在await main(10)执行完毕后才调用asyncio.all_tasks(),此时main内部创建的10个worker任务已经全部完成并被事件循环销毁,只剩当前测试用例的任务,所以断言失败。

要验证main确实创建了10个任务,需要在任务运行期间(未被销毁时)检查,以下是几种可行方案:

方案1:修改main函数返回任务集合

直接让main返回创建的worker任务集合,测试时直接断言集合大小:

修改fetcher.py的main函数:

async def main(max_requests):
    queue = asyncio.Queue(maxsize=max_requests)
    workers = {asyncio.create_task(worker(queue)) for _ in range(max_requests)}
    urls = ["https://example1.org/", "https://example2.org/", "https://example3.org/", "https://example4.org/"]
    for url in urls:
        await queue.put(url)
    for _ in range(len(workers)):
        await queue.put(None)
    await queue.join()
    return workers  # 返回创建的任务集合

测试代码修改为:

import asyncio
import pytest
from fetcher import main

@pytest.mark.asyncio
async def test_01():
    workers = await main(10)
    assert len(workers) == 10

方案2:在任务运行时同步检查

如果不想修改原代码,可以用asyncio.Event同步测试代码和被测代码的执行时机,在worker任务活跃时检查数量:

修改fetcher.py的main函数,添加可选的检查事件参数:

async def main(max_requests, check_event=None):
    queue = asyncio.Queue(maxsize=max_requests)
    workers = {asyncio.create_task(worker(queue)) for _ in range(max_requests)}
    urls = ["https://example1.org/", "https://example2.org/", "https://example3.org/", "https://example4.org/"]
    for url in urls:
        await queue.put(url)
    
    # 触发测试检查点:所有worker已创建并运行
    if check_event:
        await check_event.wait()
    
    for _ in range(len(workers)):
        await queue.put(None)
    await queue.join()

测试代码:

import asyncio
import pytest
from fetcher import main

@pytest.mark.asyncio
async def test_01():
    check_event = asyncio.Event()
    # 异步启动main,避免阻塞测试任务
    main_task = asyncio.create_task(main(10, check_event))
    
    # 等待worker任务全部创建完成
    await asyncio.sleep(0.1)
    # 获取当前循环的所有任务,过滤掉测试任务和main任务
    all_tasks = asyncio.all_tasks(asyncio.get_running_loop())
    worker_tasks = [t for t in all_tasks if t not in (asyncio.current_task(), main_task)]
    
    assert len(worker_tasks) == 10
    
    # 允许main继续执行完成
    check_event.set()
    await main_task

方案3:直接在worker中验证任务数量

如果只是要确认任务创建数量,也可以在worker函数中添加断言(适合单元测试场景):

async def worker(queue: asyncio.Queue, expected_task_count):
    while True:
        current_url = await queue.get()
        try:
            if current_url is None:
                break
            print(f"{current_url}: OK")
            # 验证当前任务数量是否符合预期
            assert len(asyncio.all_tasks()) == expected_task_count
        finally:
            queue.task_done()

然后修改main函数传递预期值,测试时只需确保main执行无异常即可。


内容的提问来源于stack exchange,提问作者zaelcovsky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 12:37:55