python环境搭建 (七) pytest、pytest-asyncio、pytest-cov 试生态的核心组合

这三个库是Python测试生态的核心组合 ,分工明确且配合无缝:pytest==9.0.2是基础测试框架(负责用例编写、执行),pytest-asyncio==1.3.0是异步测试插件(让pytest支持async def用例),pytest-cov==7.0.0是覆盖率统计插件(执行测试时统计代码被覆盖的比例),且这三个版本完全兼容,能直接搭配使用。

下面从安装→基础用例编写(同步+异步)→命令行执行(含覆盖率统计)→核心进阶用法一步步讲,所有示例开箱即用,适配你的指定版本。

一、先统一安装指定版本

打开终端,执行以下命令(国内源加速,避免下载失败),一次性安装三个库:

bash 复制代码
pip install pytest==9.0.2 pytest-asyncio==1.3.0 pytest-cov==7.0.0 -i https://pypi.tuna.tsinghua.edu.cn/simple

安装完成后,终端输入pytest --version显示9.0.2,说明环境配置成功。

二、核心规则:pytest的用例编写基础

不管是同步还是异步用例,pytest都有极简的命名规则,无需继承类、无需写繁琐的测试框架代码,符合规则的代码会被自动识别为测试用例:

  1. 测试文件命名:以test_*.py*_test.py结尾(如test_demo.py);
  2. 测试函数命名:以test_开头(如test_add());
  3. 测试类命名:以Test开头(且类名无下划线,如TestMath),类内测试方法同样以test_开头;
  4. 断言:直接用Python原生assert语句(比unittest的self.assertEqual更简洁)。

三、同步用例:纯pytest==9.0.2的基础使用

先从最简单的同步代码测试入手,理解pytest的核心用法,适合测试普通的同步函数/类(如工具函数、同步接口等)。

步骤1:编写业务代码和测试用例

新建项目目录,结构如下(简单的数学工具函数为例):

复制代码
your_project/
├── math_utils.py  # 业务代码:待测试的同步函数
└── test_math.py   # 测试文件:pytest测试用例
  • 业务代码math_utils.py

    python 复制代码
    # 待测试的同步函数
    def add(a, b):
        return a + b
    
    def sub(a, b):
        return a - b
  • 测试用例test_math.py:按pytest规则编写,直接用assert断言结果:

    python 复制代码
    from math_utils import add, sub
    
    # 测试函数:以test_开头
    def test_add():
        # 断言:预期结果=实际结果
        assert add(1, 2) == 3
        assert add(0, 0) == 0
        assert add(-1, 1) == 0
    
    def test_sub():
        assert sub(5, 3) == 2
        assert sub(2, 5) == -3
步骤2:命令行执行同步测试

打开终端,cd到项目根目录your_project/),执行核心命令:

bash 复制代码
# 最简执行:自动查找所有测试用例并执行
pytest
执行结果说明(关键信息):
  • 会显示收集到的用例数 (如collected 2 items);
  • ./F/E标识结果:.表示用例通过,F表示断言失败,E表示代码报错;
  • 最后显示总结果 (如2 passed in 0.01s),无报错即测试通过。

四、异步用例:结合pytest-asyncio==1.3.0使用

原生pytest无法直接运行async def定义的异步函数,pytest-asyncio的核心作用是让pytest支持异步用例/异步夹具(fixture) ,核心仅需一个装饰器@pytest.mark.asynciopytest-asyncio 1.3.0+无需额外配置,直接用)。

适合测试异步代码:如asyncio协程、异步IO(文件/网络)、FastAPI/Starlette异步接口、异步数据库操作等。

步骤1:编写异步业务代码和异步测试用例

在原有项目中新增异步业务文件和测试文件,结构如下:

复制代码
your_project/
├── math_utils.py       # 同步业务代码
├── async_math_utils.py # 异步业务代码:待测试的async函数
├── test_math.py        # 同步测试用例
└── test_async_math.py  # 异步测试用例:结合pytest-asyncio
  • 异步业务代码async_math_utils.py

    python 复制代码
    import asyncio
    
    # 待测试的异步函数(模拟异步操作:如异步数据库/接口调用)
    async def async_add(a, b):
        # 模拟异步延迟
        await asyncio.sleep(0.1)
        return a + b
    
    async def async_sub(a, b):
        await asyncio.sleep(0.1)
        return a - b
  • 异步测试用例test_async_math.py核心加@ pytest.mark.asyncio装饰器 ,用例直接定义为async def,断言和同步一致:

    python 复制代码
    import pytest
    from async_math_utils import async_add, async_sub
    
    # 异步测试用例:必须加@pytest.mark.asyncio装饰器
    @pytest.mark.asyncio
    async def test_async_add():
        # 异步函数需用await调用
        assert await async_add(1, 2) == 3
        assert await async_add(-2, 3) == 1
    
    @pytest.mark.asyncio
    async def test_async_sub():
        assert await async_sub(10, 4) == 6
        assert await async_sub(3, 7) == -4
步骤2:命令行执行异步测试

和同步测试命令完全一致,pytest会自动识别@pytest.mark.asyncio装饰的用例并执行异步调度:

bash 复制代码
# 执行所有测试(同步+异步)
pytest
# 仅执行异步测试文件(精准执行)
pytest test_async_math.py -v
  • -v详细模式,会显示每个用例的执行结果(推荐日常使用,更清晰);
  • 异步用例的执行速度和同步几乎一致,pytest-asyncio会自动管理事件循环,无需手动创建asyncio.run()

五、覆盖率统计:结合pytest-cov==7.0.0使用

pytest-cov是基于coverage.py封装的pytest插件,执行测试的同时自动统计代码覆盖率 ,核心统计「行覆盖率」(业务代码中多少行被测试用例执行过),能生成终端报告、HTML可视化报告(最常用),方便排查「代码死角」(未被测试覆盖的代码)。

核心命令:执行测试+统计覆盖率

所有命令均在项目根目录执行 ,核心参数是--cov=包/模块名,指定要统计的业务代码(而非测试代码),避免覆盖率被测试代码稀释。

1. 基础用法:终端显示覆盖率报告

统计math_utils.pyasync_math_utils.py的覆盖率,执行所有测试并在终端输出结果:

bash 复制代码
# --cov=./ 表示统计当前目录下所有业务代码的覆盖率(推荐简单项目)
pytest --cov=./ -v
终端报告关键信息解读:
复制代码
---------- coverage: platform win32, python 3.10.11 ----------
Name                  Stmts   Miss  Cover
-----------------------------------------
async_math_utils.py      8      0   100%
math_utils.py            4      0   100%
test_async_math.py      10      0   100%
test_math.py             8      0   100%
-----------------------------------------
TOTAL                   30      0   100%
  • Stmts:代码总行数;Miss:未被测试覆盖的行数;Cover:覆盖率;
  • 上面示例中所有代码100%覆盖,说明测试用例覆盖了所有业务逻辑。
2. 进阶用法1:排除测试文件,只统计业务代码

实际开发中,不需要统计测试文件(test_*.py)的覆盖率 ,用--cov-exclude=匹配规则排除,或直接指定业务代码目录:

bash 复制代码
# 方式1:排除所有test_*.py文件
pytest --cov=./ --cov-exclude="test_*.py" -v
# 方式2:只统计指定的业务文件(更精准)
pytest --cov=math_utils --cov=async_math_utils -v
3. 进阶用法2:生成HTML可视化报告(最推荐)

终端报告不够直观,--cov-report=html会在项目根目录生成htmlcov/文件夹,打开其中的index.html即可在浏览器中可视化查看覆盖率(红色行=未覆盖,绿色行=已覆盖),适合团队协作和项目排查:

bash 复制代码
# 核心参数:--cov-report=html 生成HTML报告
pytest --cov=./ --cov-exclude="test_*.py" --cov-report=html -v
使用HTML报告:
  1. 执行命令后,项目根目录会出现htmlcov/文件夹;
  2. 打开该文件夹中的index.html(用任意浏览器);
  3. 点击对应的业务文件(如async_math_utils.py),即可看到每行代码的覆盖情况,红色行就是需要补充测试用例的地方。
4. 精准执行:指定文件+统计覆盖率

仅测试某个文件,同时统计该文件对应的业务代码覆盖率:

bash 复制代码
# 仅执行test_async_math.py,同时统计async_math_utils的覆盖率
pytest test_async_math.py --cov=async_math_utils --cov-report=html -v

六、pytest核心进阶用法(日常开发高频使用)

结合你的指定版本,补充3个最实用的pytest功能,适配同步/异步用例,大幅提升测试效率:

1. 只执行失败的用例:--lf

当项目有大量测试用例,部分用例失败时,无需重新执行所有用例,用--lf(last failed)仅执行上一次失败的用例:

bash 复制代码
pytest --lf -v
# 结合覆盖率:只执行失败用例,同时统计覆盖率
pytest --lf --cov=./ -v
2. 标记用例并分组执行:@pytest.mark

给测试用例打标签(如@pytest.mark.unit(单元测试)、@pytest.mark.async(异步测试)),实现分组执行(如只执行单元测试,不执行异步测试),适合大型项目。

步骤1:给用例打标记

修改test_math.pytest_async_math.py,添加标记:

python 复制代码
# test_math.py 同步用例:打unit标记
import pytest
from math_utils import add, sub

@pytest.mark.unit
def test_add():
    assert add(1, 2) == 3

@pytest.mark.unit
def test_sub():
    assert sub(5, 3) == 2

# test_async_math.py 异步用例:打async标记
import pytest
from async_math_utils import async_add, async_sub

@pytest.mark.async
@pytest.mark.asyncio  # 异步用例需同时保留asyncio标记
async def test_async_add():
    assert await async_add(1, 2) == 3

@pytest.mark.async
@pytest.mark.asyncio
async def test_async_sub():
    assert await async_sub(10, 4) == 6
步骤2:分组执行用例

-m 标记名执行指定分组的用例,-m "not 标记名"排除指定分组:

bash 复制代码
# 只执行标记为unit的同步用例
pytest -m unit -v
# 只执行标记为async的异步用例
pytest -m async -v
# 执行所有用例,排除async标记的用例
pytest -m "not async" -v
3. 夹具Fixture:复用测试前置/后置逻辑

Fixture是pytest的核心特性,用于复用测试的前置操作(如初始化数据库、创建连接)和后置操作(如关闭连接、清理数据),支持同步/异步Fixture,适配pytest-asyncio。

示例:同步Fixture(复用初始化逻辑)

修改test_math.py,添加一个返回测试数据的Fixture,多个用例可复用:

python 复制代码
import pytest
from math_utils import add, sub

# 定义Fixture:以fixture装饰,scope指定作用域(function=每个用例执行一次,class=每个类,module=每个文件)
@pytest.fixture(scope="function")
def test_data():
    # 前置操作:准备测试数据
    data = {"a": 10, "b": 5}
    print("\n测试数据初始化完成")
    yield data  # yield返回数据,后续是后置操作
    # 后置操作:清理数据(如关闭连接、删除文件)
    print("\n测试数据清理完成")

# 测试用例传入Fixture名,即可使用Fixture的返回值
@pytest.mark.unit
def test_add_with_fixture(test_data):
    assert add(test_data["a"], test_data["b"]) == 15

@pytest.mark.unit
def test_sub_with_fixture(test_data):
    assert sub(test_data["a"], test_data["b"]) == 5

执行命令pytest test_math.py::test_add_with_fixture -v,会看到Fixture的前置→用例执行→后置流程自动执行。

示例:异步Fixture(结合pytest-asyncio)

修改test_async_math.py,定义异步Fixture(适配异步用例,需加@pytest.mark.asyncio):

python 复制代码
import pytest
from async_math_utils import async_add, async_sub

# 异步Fixture:async def定义,加pytest.fixture装饰
@pytest.fixture(scope="function")
async def async_test_data():
    # 异步前置操作:如异步连接数据库
    data = {"a": 20, "b": 8}
    print("\n异步测试数据初始化完成")
    yield data
    # 异步后置操作:如异步关闭数据库连接
    print("\n异步测试数据清理完成")

# 异步用例传入异步Fixture,直接await使用
@pytest.mark.async
@pytest.mark.asyncio
async def test_async_add_with_fixture(async_test_data):
    assert await async_add(async_test_data["a"], async_test_data["b"]) == 28

执行后,异步Fixture会和异步用例一起被pytest-asyncio调度,自动执行异步前置/后置逻辑。

七、三个库的组合命令汇总(日常开发直接复制)

整理最常用的同步+异步测试+覆盖率统计组合命令,覆盖80%的使用场景:

需求场景 核心命令
执行所有测试(详细模式) pytest -v
仅执行异步测试文件 pytest test_async_math.py -v
执行所有测试+终端覆盖率报告 pytest --cov=./ --cov-exclude="test_*.py" -v
执行所有测试+HTML可视化覆盖率报告 pytest --cov=./ --cov-exclude="test_*.py" --cov-report=html -v
只执行unit标记的用例+统计覆盖率 pytest -m unit --cov=math_utils --cov-report=html -v
只执行上一次失败的用例+统计覆盖率 pytest --lf --cov=./ -v

八、关键注意点(避坑,适配你的指定版本)

  1. pytest-asyncio 1.3.0 :异步用例必须加@pytest.mark.asyncio装饰器 ,无需额外配置事件循环(插件自动管理),异步Fixture直接用async def定义即可;
  2. pytest-cov 7.0.0 :统计覆盖率时一定要排除测试文件(test_*.py),否则覆盖率结果会偏高,误导排查;
  3. pytest 9.0.2 :完全兼容Python3.8+,舍弃了Python2的支持,Fixture的scope参数新增了package作用域(包级别),可按需使用;
  4. 断言失败 :pytest会自动显示预期值和实际值的差异,无需手动打印,直接根据报错信息修改用例即可。

总结

这三个库的使用核心围绕**「pytest为基础,插件做扩展」**,整体流程极简:

  1. 按规则编写同步用例test_开头)、异步用例 (加@pytest.mark.asyncioasync def函数);
  2. pytest 文件名/标记执行指定用例,加-v看详细结果;
  3. --cov系列参数结合pytest-cov统计覆盖率,--cov-report=html生成可视化报告;
  4. Fixture 复用测试前置/后置逻辑,用标记实现用例分组执行。

这套组合是Python单元测试、异步代码测试的工业级标准方案,小到工具函数测试,大到FastAPI/异步项目测试都能覆盖。

相关推荐
jghhh012 小时前
基于C#的CAN总线BMS上位机开发方案
开发语言·c#
java1234_小锋2 小时前
分享一套不错的基于Python的Django宠物信息管理系统
开发语言·python·宠物
2401_841495642 小时前
【Web开发】基于Flask搭建简单的应用网站
后端·python·flask·视图函数·应用实例·路由装饰器·调试模式
一切尽在,你来2 小时前
C++ 零基础教程 - 第4讲-实现简单计算器
开发语言·c++
女王大人万岁2 小时前
Go语言JSON标准库(encoding/json):功能解析与实战指南
服务器·开发语言·后端·golang·json
wjs20242 小时前
Scala 基础语法
开发语言
.ZGR.2 小时前
从游戏到实战的线程进阶之旅:智能无人机防空平台
java·开发语言·无人机
上海合宙LuatOS2 小时前
LuatOS ——fota 升级教程
开发语言·人工智能·单片机·嵌入式硬件·物联网·php·硬件工程
NWU_白杨2 小时前
智能无人机平台V4
java·开发语言·无人机