在 pytest 中调试异步代码有几种方法,以下是最推荐和实用的方案

方案一:使用 pytest-asyncio + breakpoint()(推荐)

1. 安装依赖

pip install pytest-asyncio

2. 在异步代码中使用 breakpoint()

import asyncio
import pytest

@pytest.mark.asyncio
async def test_async_function():
    x = await some_async_operation()
    
    # 设置断点 - 这是最简单的方法
    breakpoint()
    
    assert x == expected_value

async def some_async_operation():
    await asyncio.sleep(1)
    return "result"

3. 运行测试

# 正常运行,遇到 breakpoint() 会进入调试器 pytest -v test_file.py::test_async_function # 或者启用 pdb 模式 pytest --pdb test_file.py::test_async_function

方案二:使用 aiodebug 库(专门用于异步调试)

1. 安装

pip install aiodebug

2. 使用示例

import asyncio
import aiodebug
import pytest

@pytest.mark.asyncio
async def test_with_aiodebug():
    # 启用异步调试支持
    aiodebug.enable()
    
    result = await some_async_function()
    
    # 现在可以正常使用 pdb
    import pdb; pdb.set_trace()
    
    assert result is not None

方案三:手动创建调试事件循环

如果不想安装额外依赖,可以手动处理:

import asyncio
import pytest

@pytest.mark.asyncio
async def test_manual_debug():
    result = await some_async_function()
    
    # 手动进入调试器
    import sys
    if sys.gettrace() is not None:  # 检查是否在调试模式
        import pdb; pdb.set_trace()
    
    # 或者直接使用(在 pytest 中通常有效)
    import pdb; pdb.set_trace()
    
    assert result == "expected"

方案四:使用 VS Code 调试器(开发环境推荐)

1. 配置 .vscode/launch.json

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: pytest",
            "type": "python",
            "request": "launch",
            "program": "${env:PYTHONPATH}/bin/pytest",
            "args": [
                "-v",
                "${file}"
            ],
            "console": "integratedTerminal",
            "justMyCode": true,
            "env": {
                "PYTHONASYNCIODEBUG": "1"
            }
        }
    ]
}

2. 在代码中设置断点

直接在 VS Code 中点击行号左侧设置断点,然后按 F5 调试。

方案五:使用 debugpy(适用于远程调试)

import debugpy
import asyncio
import pytest

@pytest.mark.asyncio
async def test_with_debugpy():
    # 启动调试服务器(可选)
    # debugpy.listen(5678)
    # debugpy.wait_for_client()
    
    result = await some_async_function()
    
    # 设置断点
    debugpy.breakpoint()
    
    assert result is not None

常见问题和解决方案

问题1:pdb.set_trace() 导致事件循环挂起

原因:pdb 是同步的,会阻塞异步事件循环。

解决方案:使用 breakpoint() 或确保在 pytest-asyncio 环境中运行。

问题2:断点不生效

检查清单

  1. 确保测试函数有 @pytest.mark.asyncio 装饰器
  2. 确保安装了 pytest-asyncio
  3. 确保没有设置 PYTHONBREAKPOINT=0

问题3:需要在 fixture 中调试

@pytest.fixture
async def async_fixture():
    result = await setup_async_resource()
    
    # 可以在这里调试
    breakpoint()
    
    yield result
    
    await cleanup_async_resource()

最佳实践建议

  1. 开发阶段:使用 VS Code + pytest-asyncio 组合
  2. 命令行调试:直接使用 breakpoint() 最简单
  3. 生产调试:避免在生产代码中保留调试语句
  4. 团队协作:在 .gitignore 中添加调试相关的临时文件

完整示例

# test_async_debug.py
import asyncio
import pytest

async def fetch_data(url):
    await asyncio.sleep(0.1)  # 模拟网络请求
    return f"Data from {url}"

@pytest.mark.asyncio
async def test_fetch_data():
    url = "https://example.com"
    
    # 方法1: 使用 breakpoint() (Python 3.7+)
    breakpoint()
    
    # 方法2: 使用 pdb (传统方式)
    # import pdb; pdb.set_trace()
    
    result = await fetch_data(url)
    assert "example.com" in result

运行命令:

pytest -v test_async_debug.py::test_fetch_data

这样就能像调试同步代码一样调试异步代码了!

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐