第4章:Pytest入门——从安装到实战
自动化测试里,选对工具很重要。Pytest 是我这几年最常用的框架,没有之一。为什么?因为它简单、灵活,而且社区生态特别好。今天我就带你从零开始,把 Pytest 装好、用起来。
4.1 安装Pytest
安装这事儿,其实一句话就能说清楚。但我还是想啰嗦两句,因为我在项目里见过太多人在这栽跟头。
pip install pytest
就这么简单。不过,我建议你用虚拟环境。为什么呢?
我个人习惯这样操作:
python -m venv venv
source venv/bin/activate # Windows 用 venv\Scripts\activate
pip install pytest
装完之后,验证一下:
pytest --version
看到版本号,就说明装好了。嗯,这里要注意:如果你在公司内网,可能需要配置代理或者使用离线包。我曾经在客户现场遇到过网络不通的情况,最后是拿着 U 盘拷的 whl 文件。
4.2 基本使用:写第一个测试
Pytest 的核心理念就一个:测试就是函数。你不需要继承什么基类,也不用写一堆注解。写个函数,以 test_ 开头,就完事了。
来看个例子:
# test_demo.py
def test_addition():
assert 1 + 1 == 2
def test_subtraction():
assert 3 - 1 == 2
保存文件,然后在终端运行:
pytest test_demo.py
你会看到类似这样的输出:
============================= test session starts =============================
collected 2 items
test_demo.py .. [100%]
============================== 2 passed in 0.02s ==============================
两个点(..)代表两个测试都通过了。如果失败,会显示 F 并给出详细错误信息。
test_*.py 或 *_test.py 文件。我平时开发时,直接敲 pytest 就跑完所有测试,省事。
4.3 断言机制:不只是 assert
说到断言,很多人觉得就是 assert 关键字。没错,Pytest 确实用 Python 原生的 assert。但它做了很多增强。
举个例子:
def test_list_contains():
my_list = [1, 2, 3]
assert 4 in my_list # 这个会失败
运行后,Pytest 会告诉你:
E assert 4 in [1, 2, 3]
你看,它直接把列表内容展示出来了。这在调试时特别有用。我记得有一次排查一个复杂的 JSON 比较问题,Pytest 的断言信息帮我省了半小时。
常用的断言写法:
| 场景 | 写法 |
|---|---|
| 相等判断 | assert a == b |
| 不等判断 | assert a != b |
| 包含判断 | assert item in collection |
| 布尔判断 | assert condition |
| 异常判断 | with pytest.raises(ValueError): ... |
说到异常判断,我多说两句。你想想看,测试一个函数是否抛出预期异常,其实比测试正常路径更重要。我曾经在项目中漏测了一个异常场景,结果上线后用户一输入非法数据就崩溃……从那以后,我每条测试都至少写一个异常断言。
import pytest
def divide(a, b):
if b == 0:
raise ValueError("除数不能为0")
return a / b
def test_divide_by_zero():
with pytest.raises(ValueError, match="除数不能为0"):
divide(10, 0)
4.4 测试发现规则:Pytest 怎么找到你的测试
这个问题很关键。你写了一大堆测试文件,Pytest 怎么知道哪些是测试?规则其实很简单:
- 文件名:以
test_开头,或以_test结尾。比如test_login.py、login_test.py - 函数名:以
test_开头。比如test_user_login - 类名:以
Test开头(注意大写 T)。比如class TestLogin: - 类方法:同样以
test_开头
举个例子:
# test_user.py
def test_create_user(): # 会被发现
pass
def check_user(): # 不会被发现(没有 test_ 前缀)
pass
class TestUser:
def test_update_user(self): # 会被发现
pass
def helper_method(self): # 不会被发现
pass
运行 pytest test_user.py -v(-v 是详细模式),你会看到只发现了两个测试。
为什么会这样设计?我个人理解是:Pytest 希望你把测试代码和辅助代码分开。辅助函数不加 test_ 前缀,就不会被当成测试执行。这个设计很聪明,你想想看,如果每个函数都被当成测试,那你的工具函数怎么办?
class UserTest:(注意是 Test 结尾)。结果 Pytest 没发现它。后来查文档才知道,必须是 Test 开头。嗯,这个细节很容易忽略。
如果你想自定义发现规则,可以在 pytest.ini 或 pyproject.toml 里配置。不过说实话,我建议你保持默认。团队协作时,统一的规则比花哨的配置更重要。
4.5 实战小技巧
最后,分享几个我平时常用的技巧:
- 只运行某个测试:
pytest test_file.py::test_function - 按关键字筛选:
pytest -k "login"会运行所有名字包含 login 的测试 - 显示 print 输出:
pytest -s,调试时特别有用 - 失败时停在第一处:
pytest -x,适合快速定位问题
我记得有一次,项目里有 2000 多个测试用例,全跑完要 10 分钟。我用 -k 参数只跑跟当前改动相关的测试,把时间缩短到 30 秒。效率提升很明显。
好了,Pytest 入门就讲到这里。下一章我们会深入 fixture,这是 Pytest 最强大的功能之一。到时候我会分享一些我在实际项目中踩过的坑,保证让你少走弯路。