1. Pytest 是什么
pytest 是 Python 生态里最常用的测试框架之一,既可以做单元测试,也可以做接口测试、Web 自动化测试、数据驱动测试和测试平台集成。
它的定位不是“只会跑 assert 的工具”,而是一套完整的测试执行框架,主要提供这些能力:
- 测试用例自动发现
- 测试执行与结果汇总
- 断言增强
- 前后置管理
- 参数化
- 插件扩展
- 测试报告集成
很多团队最终选择 pytest,不是因为它“语法简单”这么单一,而是因为它把“测试组织能力”和“扩展能力”做得非常好。
2. 为什么大家喜欢用 Pytest
相比很多传统测试框架,pytest 有几个很明显的优势。
2.1 写法简单
最基础的测试只需要一个普通函数:
def test_add():
assert 1 + 1 == 2
不一定非要继承某个基类,也不一定必须写很多样板代码。
2.2 原生支持 assert
pytest 不要求你写很多类似 self.assertEqual() 的方法,而是直接支持 Python 原生 assert。
def test_user_name():
name = "tom"
assert name == "tom"
同时它会对断言失败信息做增强,方便定位问题。
2.3 fixture 机制灵活
它提供了非常强大的 fixture 机制,适合管理:
- 前置准备
- 后置清理
- 资源共享
- 依赖注入
2.4 参数化能力强
一套测试逻辑可以轻松跑多组数据。
2.5 插件生态丰富
比如:
allure-pytestpytest-xdistpytest-orderpytest-rerunfailurespytest-html
这使得它特别适合企业里的测试自动化项目。
3. Pytest 的核心工作流程
从框架视角看,pytest 大致会经历下面几个阶段:
- 读取配置
- 发现测试用例
- 收集测试节点
- 解析 fixture
- 执行测试s
- 记录结果
- 触发钩子和插件
- 输出报告
也就是说,pytest 不只是“挨个执行测试函数”,它内部其实是一个带收集、调度、注入和扩展机制的执行系统。
4. 测试发现机制
pytest 会根据命名规则自动发现测试文件和测试函数。
常见默认规则:
- 文件名:
test_*.py或*_test.py - 测试函数:
test_* - 测试类:
Test*
示例:
def test_login():
assert True
class TestUser:
def test_create_user(self):
assert 1 == 1
注意:
- 测试类通常不需要继承特定父类
- 测试类里一般不要写自定义
__init__
如果写了不符合规范的名称,pytest 可能不会收集到。
5. 断言机制
pytest 的断言能力是它非常核心的卖点。
5.1 为什么 assert 在 Pytest 里更强
普通 Python 的 assert 断言失败时,通常信息比较少。
而 pytest 会对断言表达式做重写和增强,所以当断言失败时,它会展示更详细的对比信息。
例如:
def test_compare():
assert {"code": 500} == {"code": 200}
失败时它会告诉你具体哪个字段不一致。
5.2 常见断言写法
def test_equal():
assert 200 == 200
def test_contains():
assert "ok" in "request ok"
def test_not_none():
result = {"token": "abc"}
assert result["token"] is not None
5.3 断言建议
- 断言要尽量明确
- 一个测试关注一个主要目标
- 不要把很多不相关断言堆在同一个用例里
6. fixture 机制
fixture 是 pytest 最重要的特性之一。
如果你只把它理解成“前置后置”,其实还不够。更准确地说,它是:
一种测试资源管理和依赖注入机制。
6.1 fixture 的基本用法
import pytest
@pytest.fixture
def login_token():
return "token_123"
def test_query_user(login_token):
assert login_token == "token_123"
这里的 login_token fixture 会在测试执行时自动注入到 test_query_user 中。
6.2 fixture 能做什么
它可以用来管理:
- 登录态
- 数据库连接
- 浏览器驱动
- 测试数据初始化
- 临时文件
- mock 对象
6.3 fixture 的本质
测试函数通过“参数名”声明依赖,pytest 根据参数名去找对应 fixture 并注入。
这是一种非常自然的依赖注入方式。
6.4 fixture 的作用域
常见作用域有:
functionclassmodulepackagesession
示例:
@pytest.fixture(scope="module")
def db_conn():
return "db"
含义:
function:每个测试函数执行一次class:每个测试类执行一次module:每个模块执行一次session:整个测试会话只执行一次
6.5 fixture 的前后置
如果只需要前置:
@pytest.fixture
def data():
print("初始化")
return 1
如果既要前置又要后置:
@pytest.fixture
def conn():
print("连接数据库")
yield "db_conn"
print("关闭数据库")
yield 前面的代码是前置,后面的代码是后置。
6.6 autouse 自动生效
@pytest.fixture(autouse=True)
def setup_env():
print("自动执行")
autouse=True 表示不需要显式传参,自动对当前作用域内的测试生效。
6.7 fixture 可以相互依赖
@pytest.fixture
def user():
return "tom"
@pytest.fixture
def token(user):
return f"{user}_token"
这说明 fixture 不仅能给测试函数注入,也能给其他 fixture 注入。
7. fixture 和 setup/teardown 的区别
这是面试高频题。
7.1 setup/teardown
这是更传统的前后置写法,比如:
setup_methodteardown_methodsetup_classteardown_class
它比较适合简单场景。
7.2 fixture
相比之下,fixture 更强,因为它:
- 可复用
- 可组合
- 可控制作用域
- 可依赖注入
- 可和参数化配合
7.3 面试回答模板
你可以这样说:
setup/teardown更像传统的固定前后置,而fixture是 pytest 推荐的资源管理方式。fixture 不仅能做前后置,还支持作用域控制、依赖注入和共享资源,所以在中大型测试项目里更常用。
8. 参数化 parametrize
参数化可以让一条测试逻辑执行多组数据,这是 pytest 非常常用的能力。
8.1 最基本的参数化
import pytest
@pytest.mark.parametrize("a,b,result", [
(1, 2, 3),
(2, 3, 5),
(3, 4, 7),
])
def test_add(a, b, result):
assert a + b == result
8.2 参数化的价值
- 减少重复代码
- 提高测试覆盖率
- 方便做数据驱动
8.3 和 fixture 配合
参数化不仅能作用在测试函数,也能和 fixture 一起用。
8.4 常见使用场景
- 登录接口多组用户名密码
- 搜索接口多组关键字
- 边界值测试
- 不同环境或不同浏览器组合执行
9. mark 标记机制
pytest.mark 可以给测试打标签。
9.1 自定义标记
import pytest
@pytest.mark.smoke
def test_login():
assert True
然后可以只执行某一类测试:
pytest -m smoke
9.2 常见用途
- 冒烟测试
- 回归测试
- 慢用例
- 重要模块
- 指定环境
9.3 跳过和预期失败
import pytest
@pytest.mark.skip(reason="暂时跳过")
def test_skip_case():
pass
@pytest.mark.xfail(reason="已知缺陷")
def test_known_bug():
assert 1 == 2
skip 是不执行。
xfail 是允许失败,通常表示“这个问题已知,但暂时不阻塞本次测试”。
10. conftest.py 是什么
conftest.py 是 pytest 的特殊配置文件,用来存放:
- 公共 fixture
- hook 钩子
- 全局测试控制逻辑
它的特点是:
- 不需要手工导入
- 会被当前目录及子目录下的测试自动识别
10.1 典型用途
- 定义共享 fixture
- 统一登录
- 初始化数据库
- 测试开始前清理环境
- 测试结束后收集结果
10.2 它不是 fixture 本身
这是一个很容易混淆的点。
conftest.py 是“放 fixture 的地方”,真正的 fixture 是里面带 @pytest.fixture 的函数。
11. pytest.ini 配置文件
pytest.ini 是 pytest 的配置中心。
常见可配置项:
- 测试路径
- 测试文件命名规则
- marker 注册
- 默认命令行参数
- 日志配置
例如:
[pytest]
addopts = -s -v
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
markers =
smoke: smoke test
regression: regression test
为什么要配置?
因为这样可以统一团队执行规范,避免每个人本地执行方式都不一样。
12. hook 钩子机制
pytest 内部有很多 hook,用来让你在测试执行生命周期的不同阶段插入自定义逻辑。
例如:
- 测试开始前
- 测试收集时
- 每条用例执行前后
- 测试结束汇总时
典型 hook 示例:
def pytest_terminal_summary(terminalreporter, exitstatus, config):
print("测试结束,输出汇总信息")
这类能力非常适合:
- 定制测试结果输出
- 接入通知
- 接入测试平台
- 汇总失败信息
13. Pytest 常用命令
13.1 执行全部测试
pytest
13.2 显示详细信息
pytest -v
13.3 显示 print
pytest -s
13.4 执行指定文件
pytest test_login.py
13.5 执行指定用例
pytest test_login.py::test_login_success
13.6 按关键字过滤
pytest -k login
13.7 按标记执行
pytest -m smoke
13.8 失败后停止
pytest -x
13.9 只跑失败用例
pytest --lf
14. Pytest 常见插件
14.1 allure-pytest
生成更丰富的测试报告。
14.2 pytest-xdist
支持并发执行。
命令示例:
pytest -n 4
14.3 pytest-order
控制测试执行顺序。
14.4 pytest-rerunfailures
失败用例自动重跑。
14.5 pytest-html
生成 HTML 测试报告。
插件生态丰富,是 pytest 在企业环境里非常受欢迎的重要原因。
15. Pytest 和 unittest 的区别
这是高频面试题。
15.1 写法
unittest更偏传统、规范化pytest更简洁、灵活
15.2 断言
unittest常用self.assertEqual()pytest直接用assert
15.3 前后置
unittest常用setUp/tearDownpytest更推荐fixture
15.4 参数化
pytest原生更方便unittest做参数化通常没那么自然
15.5 插件生态
pytest 更丰富。
15.6 面试总结回答
unittest更像标准库里的传统测试框架,结构规范但写法偏重;
pytest更轻量、灵活,支持更强的 fixture、参数化和插件扩展,所以在自动化测试项目里通常更常见。
16. Pytest 适合什么场景
pytest 并不只适合接口测试,它其实适合大部分 Python 测试场景:
- 单元测试
- 接口自动化测试
- Web UI 自动化测试
- 中间件测试
- 数据校验测试
- 平台级测试集成
如果项目需要:
- 数据驱动
- 复杂前后置
- 报告集成
- 与平台结合
那 pytest 往往会是一个很好的选择。
17. Pytest 的优点和局限
17.1 优点
- 简洁
- 灵活
- 可扩展
- 插件多
- fixture 强大
- 参数化方便
- 断言友好
17.2 局限
- fixture 用多了,依赖关系可能变复杂
- 新手在大型项目里容易被
conftest.py、fixture 注入和 hook 搞混 - 插件太多时,需要统一团队规范
所以 pytest 虽然强大,但在团队里也要注意测试结构设计。
18. 面试高频问题
18.1 什么是 fixture
推荐回答:
fixture 是 pytest 提供的资源管理和依赖注入机制,用来处理前后置逻辑、共享资源和测试依赖。
它比传统的 setup/teardown 更灵活,因为支持作用域、自动生效、fixture 之间依赖以及 yield 形式的清理逻辑。
18.2 conftest.py 有什么用
推荐回答:
conftest.py是 pytest 的特殊文件,用来存放公共 fixture 和 hook。它不需要显式导入,当前目录和子目录下的测试会自动识别它。
18.3 Pytest 如何做参数化
推荐回答:
Pytest 通过
@pytest.mark.parametrize实现参数化,可以把一条测试逻辑和多组测试数据组合起来执行,非常适合数据驱动测试。
18.4 fixture 和 setup/teardown 有什么区别
推荐回答:
setup/teardown更偏传统前后置,能力相对固定;fixture 不仅能做前后置,还支持依赖注入、作用域控制和共享资源,扩展性更强。
18.5 为什么 Pytest 适合自动化测试
推荐回答:
因为它支持测试发现、参数化、fixture、hook 和丰富插件,能够很好地和 requests、selenium、allure、数据库、CI 平台结合,所以非常适合自动化测试框架建设。
19. 学习 Pytest 时最应该掌握什么
如果要真正学会 pytest,建议优先掌握这几块:
- 测试发现规则
assert断言- fixture
parametrizeconftest.pypytest.ini- 常用命令
- 常用插件
这几块掌握了,已经可以覆盖绝大多数实际工作场景。
20. 一句话总结
pytest 不是“一个能跑测试函数的小工具”,而是一套围绕测试发现、依赖注入、参数化、生命周期控制和插件扩展构建起来的完整测试框架。
它之所以流行,是因为它既适合写简单测试,也足够强大,能支撑复杂的自动化测试工程。