pytest通过命名规则自动识别测试:文件名须以test_开头或_test.py结尾,函数名须以test_开头,类名须以test开头且无__init__方法;不满足则被忽略。

pytest 能直接运行函数、自动发现测试用例、支持参数化和 fixture,比 unittest 更轻量且表达力更强——但不按约定写,它就找不到你的测试。
pytest 怎么识别哪些函数是测试
pytest 通过命名规则自动发现测试:函数名必须以 test_ 开头,或文件名以 test_ 开头(或以 _test.py 结尾)。类名也得是 Test* 形式,且不能带 __init__ 方法。
常见错误现象:
- 写了
def check_add():—— pytest 直接忽略 - 文件叫
math_utils.py—— 不会扫描其中的test_add - 类叫
CalculatorTests(末尾多 s)—— 不被识别为测试类
实操建议:
- 统一用
test_*.py命名文件,比如test_calculator.py - 测试函数写成
def test_add_two_positive_numbers():,别缩写 - 如果非要用自定义前缀,可通过
python -m pytest --collect-only验证是否被识别
如何给测试传不同参数(避免重复写多个 test 函数)
用 @pytest.mark.parametrize 是最常用的方式,比手写循环清晰,失败时还能单独定位哪组参数出错。
使用场景:验证同一函数在边界值、异常输入、多种类型下的行为。
示例:
@pytest.mark.parametrize("a,b,expected", [
(1, 2, 3),
(-1, 1, 0),
(0, 0, 0),
])
def test_add(a, b, expected):
assert add(a, b) == expected
注意点:
- 参数名列表(
"a,b,expected")必须和函数签名完全一致,顺序敏感 - 传入
None或嵌套结构(如字典)没问题,但别传未定义变量 - 如果某组参数预期抛异常,改用
pytest.raises()配合with块,别指望 parametrize 自动捕获
fixture 怎么写才不污染测试逻辑
fixture 是 pytest 的核心机制,用于准备/清理测试依赖(比如临时文件、数据库连接、mock 对象),但它不是“全局 setup”,作用域要明确控制。
关键参数:
-
scope="function"(默认):每个测试函数执行前调用一次 -
scope="module":整个文件只初始化一次,适合开销大的资源 -
autouse=False(默认):必须显式声明参数才能注入,避免隐式依赖
实操建议:
- 不要在 fixture 里做断言或修改被测对象状态
- 清理逻辑务必写在
yield后面(或用addfinalizer),否则异常时不会执行 - 避免跨 scope 共享 mutable 对象(比如 list、dict),容易被一个测试改乱影响下一个
简单例子:
@pytest.fixture
def temp_db():
db = create_test_db()
yield db
db.close() # 这行一定会执行
为什么 pytest.main() 在脚本里跑不通
直接在 Python 文件里调用 pytest.main() 很常见,但容易踩两个坑:路径问题和参数解析失败。
常见错误现象:
- 报错
FileNotFoundError: No module named 'test_xxx'—— 因为当前工作目录不对 - 传参写成
pytest.main(["-v", "test_math.py"])却没找到文件 —— pytest 默认从sys.path[0]查找,不是当前脚本所在目录
实操建议:
- 优先用命令行运行:
python -m pytest test_math.py -v - 如果必须用
pytest.main(),加args并指定cwd:pytest.main(args=["-v", "test_math.py"], plugins=[...]) - 避免在测试文件内部调用
pytest.main(),这会导致递归收集,极难调试
复杂点在于 fixture 的生命周期和参数化组合会指数级放大测试用例数,而报错堆栈默认不显示完整参数。真要 debug,得加 -vv 和 --tb=short 控制输出,不然一眼看不到底。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











