3. pytest入门:命名规则、断言机制、运行模式、测试用例编写
好,咱们今天来聊聊pytest。说实话,我刚开始做自动化测试那会儿,用的还是unittest。后来有一次接手一个大型项目,用例数量上千,unittest跑起来那个慢啊……同事推荐我试试pytest,一试就回不去了。pytest给我的感觉就是——简洁、灵活、强大。你想想看,一个框架能同时搞定单元测试和端到端测试,还能跟各种插件无缝集成,这得多省心。
3.1 pytest的命名规则
pytest最方便的一点,就是它的自动发现机制。你不需要手动注册用例,它自己就能找到。但前提是——你得按它的规矩来命名。
核心规则:
- 测试文件:以
test_开头,或以_test结尾。比如test_login.py、login_test.py - 测试类:以
Test开头。注意首字母大写,比如TestUserLogin - 测试方法/函数:以
test_开头。比如test_login_success
我个人习惯把测试文件命名为 test_模块名.py,这样一眼就能看出测的是哪个模块。比如测登录功能,就叫 test_login.py。嗯,这里要注意:测试类里不要写 __init__ 方法,否则pytest会报错。我刚开始就踩过这个坑。
# test_demo.py
class TestCalculator:
def test_add(self):
assert 1 + 1 == 2
def test_subtract(self):
assert 3 - 1 == 2
3.2 断言机制
pytest的断言,说白了就是用Python原生的 assert 关键字。这一点跟unittest那种 self.assertEqual() 的写法比起来,简直不要太清爽。
为什么会这样?因为pytest在底层对 assert 做了增强。当断言失败时,它会自动输出详细的上下文信息,告诉你实际值和期望值差了多少。我在项目中遇到过好几次,一个断言失败,pytest直接打印出两个列表的差异,省了我大量调试时间。
常用断言写法:
assert a == b—— 判断相等assert a != b—— 判断不等assert a in b—— 判断包含assert a is None—— 判断为空assert a > 0—— 判断大小assert bool(a) is True—— 判断布尔值
def test_list_contains():
my_list = [1, 2, 3, 4, 5]
assert 3 in my_list # 判断元素是否在列表中
def test_string_contains():
text = "Hello, pytest!"
assert "pytest" in text # 判断子串
def test_dictionary():
user = {"name": "Alice", "age": 30}
assert user["age"] == 30
assert "name" in user # 判断键是否存在
避坑指南:我曾经在断言浮点数相等时吃过亏。两个浮点数明明看起来一样,但断言就是失败。后来才发现是精度问题。建议用 pytest.approx() 来处理浮点数比较。
def test_float():
assert 0.1 + 0.2 == pytest.approx(0.3) # 使用approx避免精度问题
3.3 运行模式
pytest的运行模式非常灵活。你可以跑单个文件、单个函数,甚至按标签筛选。我平时最常用的就这几种:
| 命令 | 说明 | 示例 |
|---|---|---|
pytest |
运行当前目录下所有测试 | 直接执行,自动发现 |
pytest 文件名.py |
运行指定文件 | pytest test_login.py |
pytest -k "关键字" |
按名称匹配运行 | pytest -k "login" |
pytest -v |
显示详细输出 | 每个用例名称都会打印 |
pytest -s |
显示print输出 | 调试时特别有用 |
pytest --tb=short |
缩短错误回溯 | 只看关键错误信息 |
举个例子,你只想跑登录相关的用例,但又不想改代码,直接用 pytest -k "login" -v 就行。我个人习惯在调试时加上 -s 参数,这样能看到print的中间结果,排查问题快很多。
3.4 测试用例编写
写测试用例,说白了就是三步:准备数据、执行操作、验证结果。pytest让这三步变得特别自然。
先看一个最简单的例子:
# test_calculator.py
def test_add():
# 准备数据
a, b = 3, 5
# 执行操作
result = a + b
# 验证结果
assert result == 8
def test_subtract():
assert 10 - 4 == 6
你可能会问:就这么简单?对,就这么简单。pytest不强制你用类,直接写函数就行。当然,如果用例之间有依赖关系,或者需要共享一些数据,那就用类来组织。
用类组织用例的好处:
- 相关用例放在一起,结构清晰
- 可以用
setup_method做前置准备 - 方便用
@pytest.mark打标签
class TestUserAPI:
def setup_method(self):
# 每个测试方法执行前都会运行
self.user_data = {"name": "test_user", "age": 25}
def test_create_user(self):
# 模拟创建用户
user = create_user(self.user_data)
assert user["name"] == "test_user"
assert user["age"] == 25
def test_update_user(self):
# 模拟更新用户
updated = update_user(1, {"age": 26})
assert updated["age"] == 26
我的经验:写测试用例时,尽量做到一个用例只测一个功能点。比如测登录,就分三个用例:成功登录、密码错误、账号不存在。这样哪个环节出问题,一眼就能定位。我曾经见过有人一个用例里写了十几个断言,失败了都不知道是哪个条件没满足……
最后,别忘了pytest的标签功能。你可以给用例打上 @pytest.mark.smoke、@pytest.mark.regression 这样的标签,然后按标签运行:
import pytest
@pytest.mark.smoke
def test_login_success():
assert login("admin", "123456") == True
@pytest.mark.regression
def test_login_failed():
assert login("admin", "wrong") == False
运行命令:pytest -m smoke,就只跑冒烟测试的用例。这个在持续集成里特别实用。
好了,pytest入门就聊到这儿。记住:命名规则别搞错,断言用assert,运行模式按需选,用例结构要清晰。下一章咱们聊聊fixture,那才是pytest真正强大的地方。