pytest 是 Python 生态中最流行的单元测试框架之一,用于编写、组织和运行自动化测试代码。广泛应用于功能测试、接口测试、单元测试等场景。
pytest的用例规则是怎么样的?
默认发现规则(即零配置即可生效)文件命名规则
- 必须满足前缀或者后缀是test,非匹配文件会被忽略:
test_*.py
(如 test_login.py
)
*_test.py
(如 login_test.py
)
- 测试函数:必须以
test_
开头
- 测试类:类名以
Test
开头,且不能有 __init__
方法
- 方法名以
test_
开头
⚙️ 自定义规则(通过 pytest.ini
修改)
1 2 3 4 5
| [pytest] python_files = check_*.py python_classes = *Test python_functions = verify_*
|
📂 目录结构规范
1 2 3 4 5 6 7 8 9
| project/ ├── tests/ │ ├── __init__.py │ ├── test_web/ │ │ ├── test_login.py │ │ └── test_api.py │ └── test_db.py ├── src/ └── pytest.ini
|
🔍 基础执行控制
参数 |
作用 |
示例 |
-v / --verbose |
显示详细输出(用例名称+结果) |
pytest -v |
-q / --quiet |
简化输出(仅显示结果摘要) |
pytest -q |
-x |
遇到第一个失败后立即停止 |
pytest -x |
--maxfail=n |
允许最多 n 次失败后停止 |
pytest --maxfail=2 |
-k "表达式" |
模糊匹配用例名/类名 |
pytest -k "login" |
-m "标记" |
运行指定标记的用例 |
pytest -m "slow" |
标记(Mark)
标记的核心作用是灵活控制测试的执行逻辑,比如跳过某些测试、指定测试优先级、分组运行等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import pytest
@pytest.mark.smoke def test_login(): assert True
@pytest.mark.performance def test_load(): assert 1 + 1 == 2
pytest -m "smoke" pytest -m "not performance"
|
自定义标记
在 pytest.ini
中声明标记:
1 2 3 4 5 6 7 8 9 10
| [pytest] markers = smoke: 冒烟测试用例 performance: 性能测试 slow: 慢速测试(需额外时间)
@pytest.mark.slow def test_big_data_processing(): assert process_data() == expected
|
内置标记
pytest 提供内置标记,它们无需额外声明即可直接使用,专门用于处理常见的测试场景。内置标记无需注册,直接使用。同时优先级也会更高,会覆盖自定义标记的同名行为。
特性 |
内置标记 |
自定义标记 |
是否需要声明 |
❌ 直接使用 |
✅ 需在 pytest.ini 声明 |
是否可修改行为 |
❌ 固定逻辑 |
✅ 可自由定义 |
典型用途 |
跳过/参数化/预期失败等标准场景 |
业务分组(如smoke/regression) |
📌 pytest 主要内置标记
1. 跳过测试相关
标记 |
作用 |
@pytest.mark.skip |
无条件跳过测试用例 |
@pytest.mark.skipif |
满足条件时跳过(需指定条件表达式) |
@pytest.mark.xfail |
标记预期失败的测试(失败时输出XFAIL,意外通过则XPASS) |
@pytest.mark.xfail(strict=True) |
严格模式:如果意外通过则报错(用于确保修复后移除标记) |
@pytest.mark.parametrize |
多组参数驱动同一测试 |
内置标记使用场景
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import sys
@pytest.mark.skipif( sys.platform != "linux", reason="仅Linux环境测试" ) def test_linux_syscall(): assert os.system("ls") == 0
@pytest.mark.skipif( pandas.__version__ < "2.0.0", reason="需要pandas 2.0+" )
|
夹具(Fixture)
用于为测试用例提供预置条件和清理操作的核心机制,相当于测试的「脚手架」。通过 @pytest.fixture
装饰器定义,减少重复代码并提升测试的可维护性。
🎯 夹具的核心作用
- 初始化资源:如数据库连接、测试数据
- 共享复用代码:避免重复编写 setup/teardown
- 依赖注入:自动将夹具传递给测试函数
- 保证清理:无论测试成功/失败都会执行清理
1 2 3 4 5 6 7 8 9 10 11 12
| import pytest
@pytest.fixture def my_fixture(): data = {"key": "value"} yield data
def test_example(my_fixture): assert my_fixture["key"] == "value"
|
夹具的作用域控制
1 2 3 4 5 6 7 8 9
| @pytest.fixture(scope="function") @pytest.fixture(scope="class") @pytest.fixture(scope="module") @pytest.fixture(scope="session")
@pytest.fixture(autouse=True) def auto_fixture(): print("\n这个夹具会自动在每个测试中运行")
|
夹具可以依赖其他夹具
1 2 3 4 5 6 7 8 9 10 11
| @pytest.fixture def db_connection(): conn = create_db_connection() yield conn conn.close()
@pytest.fixture def user_table(db_connection): db_connection.create_table("users") yield db_connection.drop_table("users")
|
夹具工厂
当需要参数化夹具时,可以使用夹具工厂模式:
1 2 3 4 5 6 7 8 9
| @pytest.fixture def make_user(): def _make_user(name, age): return {"name": name, "age": age} return _make_user
def test_user_factory(make_user): user = make_user("Alice", 25) assert user["name"] == "Alice"
|
插件
插件分为两类,一类是内置的,一类是第三方。
使用的方式也很简单,-p xxx是启用,-p no xxx是禁用。
插件一般使用方法都是通过上面的方法:参数,配置文件,夹具,mark