📌 项目地址pytest-dev/pytest | ⭐ 13,989 颗星 | 🔧 Python | 📜 未标注

为什么需要pytest

Python开发者都知道,写测试是保证代码质量的关键。但传统的unittest要求你写一堆样板代码:定义类、继承TestCase、用self.assertEqual等方法。如果测试逻辑复杂,这些额外结构反而分散了精力。

pytest的出现改变了这一切。它的核心理念是:用最少的代码写最直的测试。你只需要写普通的函数,用assert做断言,然后运行pytest。小到单文件脚本,大到几千个测试用例的项目,它都能胜任。

快速上手:两条命令写出第一个测试

根据官方README,安装pytest只需一条命令(推荐使用虚拟环境):

pip install -U pytest

新建一个测试文件test_sample.py,写入一个函数:

# content of test_sample.py
def func(x):
    return x + 1

def test_answer():
    assert func(3) == 5

然后在终端运行:

pytest

你会看到清晰的输出,指出哪一行断言失败、实际值和预期值是什么。整个过程不需要继承任何类,不需要调用任何测试运行器。

这就是pytest最直观的魅力:用Python原生的assert,加上自动发现所有test_开头的函数

核心特性:能平替unittest的几大杀器

1. Fixture——替代setUp/tearDown的更优雅方案

unittest中,每个测试类都需要写setUptearDown方法,如果多个测试类共享资源,还得用setUpClasssetUpModule。pytest的fixture机制则灵活得多:

import pytest

@pytest.fixture
def input_value():
    return 39

def test_divisible_by_3(input_value):
    assert input_value % 3 == 0

fixture可以自动注入到测试函数中,作用范围通过scope参数控制(function/class/module/session),可以返回任何Python对象,还可以互相依赖。

2. 参数化测试——消除重复代码

当需要测试多组输入数据时,unittest要么写多个测试方法,要么用subTest。pytest提供了@pytest.mark.parametrize装饰器:

@pytest.mark.parametrize("test_input,expected", [
    ("3+5", 8),
    ("2+4", 6),
    ("6*9", 42),   # 这个会失败,展示错误
])
def test_eval(test_input, expected):
    assert eval(test_input) == expected

一行装饰器就能生成多个测试用例,每个失败独立显示。

3. 丰富的插件生态

pytest有500+插件,官方文档列出了分类。例如:
pytest-cov:集成覆盖率统计
pytest-xdist:并行执行测试
pytest-mock:无缝使用Mock对象
pytest-djangopytest-flask:框架专用插件

插件通过pip install安装后自动启用,无需额外配置(大多数情况下)。

4. 自动发现与运行控制

默认从当前目录开始递归查找test_*.py*_test.py文件,并运行其中的test_*函数和Test*类中的方法。也可以通过-k选择测试:

pytest -k "not slow"   # 跳过标记为slow的测试
pytest -x              # 首次失败后停止
pytest --lf            # 只运行上次失败的测试

与unittest的区别:为什么你该考虑切换

维度 unittest pytest
编写方式 需继承TestCase,使用assertEqual等方法 纯函数+assert语句
测试发现 需手动调用unittest.main(),默认只发现Test* 自动递归发现
资源管理 setUp/tearDown,作用域固定 fixture,可自定义作用域和依赖
参数化 不支持原生,需借用subTest或第三方库 内置@pytest.mark.parametrize
插件系统 有但不够丰富 数百个官方/社区插件
错误报告 抛出异常,信息简略 彩色输出,显示具体变量值

对于已有unittest代码,pytest提供了兼容模式:可以直接运行unittest.TestCase派生的测试,并享受pytest的插件和输出格式。也就是零迁移成本

注意事项与局限性

  1. Python版本要求:pytest 7.x 需要 Python 3.7+,pytest 8.x 需要 Python 3.8+(根据README中setup.py的python_requires)。如果你的项目还在用Python 2.7或3.6,则无法使用最新版。
  2. 隐式fixture依赖:fixture通过参数名称自动注入,如果参数名拼写错误,pytest会报告缺少 fixture,这个错误提示比较明确,但刚上手时可能不习惯。
  3. 插件冲突:少数插件之间可能存在兼容性问题,建议在项目requirements.txt中固定pytest版本。
  4. 大规模测试的性能:相比unittest,pytest自动发现和fixture构建有一定性能开销。但对于大多数项目(数千测试以内)完全无感。如果超过万级别,可以用pytest-xdist并行或--co缓存fixture结果。
  5. 非标准测试结构:pytest默认发现规则是基于文件/函数/类命名模式的。如果你的测试文件放在不合规的目录里,需要添加conftest.py或通过--ignore排除。

适宜人群与项目类型

pytest适合任何需要写Python测试的项目:从个人小工具到大型多模块系统、从纯Python库到Django/Flask Web应用、从数据科学脚本到机器学习模型评估。它不挑项目,因为它只负责“运行测试”这件事,而测试内容(数据库、网络、UI等)由其他库或插件处理。

如果你正在维护一个长期项目,以往惯用unittest,那么切换为pytest可以显著减少样板代码、提升可读性,并享受丰富的第三方插件。即使不切换,pytest也能作为运行器直接执行你的unittest用例。

参考链接

这篇文章对你有帮助吗?

发表回复