4.7 KiB
4.7 KiB
Pytest 核心概念指南
1. 断言详解 (Assertions)
pytest 直接使用 Python 原生的 assert 关键字,不需要引入额外的断言库。
# 1. 基本形式
assert condition
# 2. 带自定义错误信息
assert condition, "Error message if assertion fails"
注意:断言主要用于开发和测试阶段,不要在生产代码的关键业务逻辑中仅依赖断言(因为 python -O 运行时会忽略断言)。
2. 发现规则 (Discovery Rules)
pytest 默认按照以下规则搜索测试:
文件名规则
test_*.py(以test_开头的 Python 文件)*_test.py(以_test结尾的 Python 文件)
函数与类名规则
def test_something(): # 以 test_ 开头的函数
assert 1 + 1 == 2
class TestSomething: # 以 Test 开头的类
def test_method(self): # 以 test_ 开头的方法
assert True
运行方式
# 1. 自动搜索:在当前目录及其子目录中搜索
pytest
# 2. 指定目录
pytest tests/
pytest src/
# 3. 指定特定文件
pytest main.py
# 4. 运行特定测试函数 (文件::函数)
pytest tests/test_basic.py::test_addition
3. 测试函数 (Test Functions)
测试函数是 pytest 的最基本单元。
基本特征
- 函数名必须以
test_开头。 - 通常不接受参数(除非使用
fixture或parametrize)。 - 使用
assert语句进行断言。 - 通过返回值或异常来表示测试结果。
断言与异常处理
A. 普通断言 (期望成功)
验证代码逻辑是否符合预期。
def test_basic():
assert 1 == 1 # 如果条件为 False,测试失败
B. 异常断言 (期望抛出错误)
验证代码在错误输入下是否正确抛出了异常(使用 pytest.raises)。
import pytest
def divide(a, b):
if b == 0:
raise ValueError("b cannot be 0")
return a / b
def test_divide_zero():
# 断言:执行 divide(1, 0) 时应该抛出 ValueError
with pytest.raises(ValueError):
divide(1, 0)
4. Fixture 夹具 (Fixtures)
Fixture 用于处理测试前置准备(Setup)和后置清理(Teardown),以及数据共享。
基本用法
import pytest
@pytest.fixture
def sample_data():
"""返回测试数据"""
print('>>> Setup')
# yield 之前的代码相当于 Setup
yield {"name": "Alice", "age": 30, "city": "New York"}
# yield 之后的代码相当于 Teardown
print('<<< Teardown')
def test_user_name(sample_data):
# sample_data 作为参数传入,获取 yield 返回的值
assert sample_data["name"] == "Alice"
提示:要看到 fixture 中的 print 输出,运行时需要加
-s参数:pytest -s
Fixture 作用域 (Scope)
通过 @pytest.fixture(scope="...") 指定共享范围:
| 作用域 | 说明 | 典型应用场景 |
|---|---|---|
| function (默认) | 每个测试函数运行一次 | 独立的数据、清理数据库记录 |
| class | 每个测试类运行一次 | 测试类共享的配置 |
| module | 每个模块 (.py文件) 运行一次 |
模块级别的连接 |
| session | 整个测试会话只运行一次 | 全局配置、浏览器实例、数据库连接池 |
5. Mark 标记 (Marks)
使用 Mark 给测试分类,便于筛选运行。
定义标记
import pytest
@pytest.mark.p1
def test_feature_a():
assert True
@pytest.mark.slow
def test_network_op():
pass
@pytest.mark.p1
@pytest.mark.slow
def test_complex():
pass
运行指定标记
使用 -m 参数支持逻辑表达式:
# 运行标记为 p1 的测试
pytest -m "p1"
# 运行标记为 p1 或 slow 的测试
pytest -m "p1 or slow"
# 运行同时标记为 p1 和 slow 的测试
pytest -m "p1 and slow"
# 运行不包含 p1 标记的测试
pytest -m "not p1"
6. 参数化 (Parametrize)
使用 @pytest.mark.parametrize 实现数据驱动测试,用多组数据测试同一个逻辑。
import pytest
# 定义参数名和参数值列表
@pytest.mark.parametrize("x, y, expected", [
(1, 2, 3),
(4, 5, 9),
(10, 20, 30),
])
def test_addition(x, y, expected):
assert x + y == expected
7. 常用命令行参数
| 命令 | 作用 | 示例 |
|---|---|---|
pytest -v |
Verbose,显示详细输出 | 查看每个测试函数的执行结果 |
pytest -s |
Standard Output,捕获/显示标准输出 | 查看 print 语句的输出 |
pytest -m |
Marker,根据标记运行 | pytest -m "smoke" |
pytest -k |
Keyword,根据名称关键字运行 | pytest -k "user or login" |
pytest -n |
Num processes,多进程并行运行 | pytest -n 4 (需要 pytest-xdist 插件) |