文章目录
-
- 前言
- 一、环境准备
- 二、最小可运行示例
- 三、核心概念
-
- [1. 自动发现](#1. 自动发现)
- [2. 断言即测试](#2. 断言即测试)
- [3. Fixture](#3. Fixture)
- [四、进阶用法:fixture 与参数化](#四、进阶用法:fixture 与参数化)
- 五、实战场景:测试带异常的代码
- [六、常见坑 / 报错](#六、常见坑 / 报错)
-
- [1. collected 0 items](#1. collected 0 items)
- [2. fixture 'xxx' not found](#2. fixture 'xxx' not found)
- [3. 断言失败但看不出原因](#3. 断言失败但看不出原因)
- 七、总结与下一步
P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看, 传送门https://blog.csdn.net/qq_34419312
前言
写代码不写测试,跟洗完澡不关水龙头一个性质:当时觉得没事,等水流一地才开始后悔。你改一行代码,崩了三处功能,然后在报错堆里灵魂三问------我是谁?我在哪?我昨天到底改了什么?
Python 标准库其实有个 unittest,用起来像在写论文:样板代码比业务代码还长。而 pytest 主打一个"少废话、多办事"------更少的样板代码、更聪明的断言、一堆现成的插件,社区主流没跑了。
一、环境准备
装它,一条命令的事,比下楼取快递还快:
pip install pytest
装完顺手验证一下:
pytest --version
要求 Python 3.8+。版本太老的朋友建议先升级,别问我怎么知道的,往事不堪回首。
最关键的一点:装完不用配任何东西。pytest 的哲学是"约定大于配置"------它默认你是个守规矩的人,你只要守规矩就行。
二、最小可运行示例
假设你有个业务函数,放在 calc.py 里,就那种"算个加法都怕算错"的函数:
python
# calc.py
def add(a, b):
return a + b
然后写测试,文件叫 test_calc.py。注意暗号:文件名要以 test_ 开头,函数名也要以 test_ 开头,接不上头它就当你不存在。
python
# test_calc.py
from calc import add
def test_add_int():
assert add(1, 2) == 3
def test_add_str():
assert add("a", "b") == "ab"
终端敲一个 pytest,pytest 自动把所有 test_* 用例捡走执行。绿色 PASSED 亮起那一刻,你会产生"我也是会写测试的人"的错觉。别高兴太早,是错觉,坑在后面排队呢。
三、核心概念
1. 自动发现
默认找 test_*.py 或 *_test.py,里面的函数/类以 test_ 开头。也就是说,你只要把名字起对,pytest 就像饿了三天突然看到外卖的狗,自己就冲过去了。
2. 断言即测试
直接写 Python 原生的 assert,失败时它自动把变量值给你打出来,明明白白。不用像 unittest 那样写 self.assertEqual,省下来的时间够你多刷一个短视频。
3. Fixture
用 @pytest.fixture 准备测试前置数据/资源,可复用、可依赖。翻译成人话:就是"开考前先帮你把草稿纸和笔备好"的工具人。
四、进阶用法:fixture 与参数化
fixture 帮你共享数据库连接,参数化让你一条用例测多组输入------用最少的代码,得罪最多的 bug:
python
import pytest
@pytest.fixture
def sample_data():
return [1, 2, 3, 4]
def test_sum(sample_data):
assert sum(sample_data) == 10
@pytest.mark.parametrize("a,b,expect", [
(1, 1, 2), (0, 0, 0), (-1, 1, 0),
])
def test_add(a, b, expect):
assert add(a, b) == expect
看到没,一个函数跑出三组用例,报告还给你列得清清楚楚。这叫什么?这叫花一份工资,雇三个质检员。
五、实战场景:测试带异常的代码
有些代码你希望它"该出错时就出错",比如除数为零,它要是没报错你才要慌。验证方式如下:
python
import pytest
def divide(a, b):
if b == 0:
raise ValueError("b 不能为 0")
return a / b
def test_divide_zero():
with pytest.raises(ValueError):
divide(1, 0)
pytest.raises 这个上下文管理器专门盯异常,类型对不上它就给你脸色看。这是测试防御性代码的利器------相当于给代码装了安全气囊,平时看不见,出事才觉得真香。
六、常见坑 / 报错
1. collected 0 items
pytest 没找到用例,一个都没捡到。先别怀疑人生,检查文件名/函数名是不是以 test_ 开头,或者你是不是在正确的目录下运行。多数时候不是你代码不行,是你没对上暗号。
2. fixture 'xxx' not found
引用的 fixture 没定义,或者定义在别的文件里没被发现。公共 fixture 请放进 conftest.py,放那里面就是全剧组的共用道具,谁都能用。
3. 断言失败但看不出原因
用 assert a == b 而不是自定义比较,pytest 会把两侧的实际值都打印出来,证据确凿,赖不掉。复杂对象做浮点比较就用 pytest.approx:assert val == pytest.approx(0.1)。别问为什么 0.1+0.2 不等于 0.3,问就是二进制不想理你。
七、总结与下一步
pytest 用极简写法撬动专业测试:自动发现、智能断言、fixture 复用、参数化覆盖。基本就四板斧,但够用。
下一步建议:
- 加
pytest-cov看测试覆盖率,看看你嘴上说的"测过了"到底覆盖了多少行。 - 用
pytest-mock隔离外部依赖,把网络、数据库这些"猪队友"换成替身演员。 - 接入 CI(比如 GitHub Actions),让每次提交自动跑测试。从此以后,代码合不合规机器说了算,不用你半夜爬起来手动跑。
最后说句掏心窝的:写好 pytest,你的 Python 代码才真正"稳得住"。不然哪天改了个配置,半夜线上报警,你端着保温杯坐电脑前,那种酸爽,比老坛酸菜还正宗。
P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看,传送门https://blog.csdn.net/qq_34419312