pytest API 参考指南:从核心功能到高级用法详解
概述
pytest 是 Python 生态中最流行的测试框架之一,以其简洁的语法和强大的功能著称。本文将全面介绍 pytest 的核心 API,帮助开发者更好地理解和使用这个框架。
核心常量
版本信息
pytest 提供了两种方式获取版本信息:
import pytest
# 字符串形式版本号
print(pytest.__version__) # 示例输出: '7.0.0'
# 元组形式版本号
print(pytest.version_tuple) # 示例输出: (7, 0, 0)
对于预发布版本,元组的最后一个元素会是字符串形式,如 (7, 0, '0rc1')
。
隐藏参数标识
pytest.HIDDEN_PARAM
是一个特殊标识,可用于隐藏参数化测试中的某些参数组合:
@pytest.mark.parametrize("x", [1, 2], ids=["visible", pytest.HIDDEN_PARAM])
def test_example(x):
pass
核心功能函数
测试控制函数
-
pytest.fail() - 强制使测试失败
def test_something(): if not condition: pytest.fail("明确说明失败原因")
-
pytest.skip() - 跳过当前测试
def test_requires_special_env(): if not has_special_env(): pytest.skip("需要特殊环境才能运行")
-
pytest.xfail() - 标记预期失败的测试
def test_experimental_feature(): pytest.xfail("此功能尚未完善")
-
pytest.exit() - 终止测试会话
if critical_error: pytest.exit("关键错误,终止所有测试")
断言与异常处理
-
pytest.raises() - 验证代码是否抛出预期异常
def test_division_by_zero(): with pytest.raises(ZeroDivisionError): 1 / 0
-
pytest.approx() - 浮点数近似比较
def test_floating_point(): assert 0.1 + 0.2 == pytest.approx(0.3)
其他实用函数
-
pytest.importorskip() - 条件导入模块
numpy = pytest.importorskip("numpy")
-
pytest.param() - 参数化测试中的参数封装
@pytest.mark.parametrize("x", [pytest.param(1, id="case1")]) def test(x): pass
标记(Mark)系统
pytest 的标记系统允许为测试函数添加元数据,这些标记可以被夹具或插件访问。
内置标记
-
@pytest.mark.skip - 无条件跳过测试
@pytest.mark.skip(reason="尚未实现") def test_feature(): pass
-
@pytest.mark.skipif - 条件跳过测试
@pytest.mark.skipif(sys.version_info < (3, 8), reason="需要Python 3.8+") def test_new_syntax(): pass
-
@pytest.mark.parametrize - 参数化测试
@pytest.mark.parametrize("input,expected", [(1, 2), (3, 4)]) def test_increment(input, expected): assert input + 1 == expected
-
@pytest.mark.usefixtures - 使用指定夹具
@pytest.mark.usefixtures("cleandir") def test_file_operations(): pass
自定义标记
开发者可以创建自己的标记:
@pytest.mark.slow
def test_long_running():
pass
这些标记可以通过 pytest.mark
工厂对象动态创建,并附加到测试项上。
夹具(Fixture)系统
夹具是 pytest 最强大的功能之一,用于提供测试所需的依赖项。
基本用法
@pytest.fixture
def database():
db = setup_database()
yield db
teardown_database(db)
def test_query(database):
result = database.query("SELECT 1")
assert result == 1
内置夹具
-
tmp_path - 临时目录路径(Path对象)
def test_create_file(tmp_path): file = tmp_path / "test.txt" file.write_text("content") assert file.read_text() == "content"
-
monkeypatch - 临时修改环境
def test_env_var(monkeypatch): monkeypatch.setenv("DEBUG", "1") assert os.getenv("DEBUG") == "1"
-
capsys - 捕获标准输出/错误
def test_print(capsys): print("hello") captured = capsys.readouterr() assert captured.out == "hello\n"
-
caplog - 捕获日志
def test_logging(caplog): logging.warning("test message") assert "test message" in caplog.text
钩子(Hook)系统
钩子系统允许开发者扩展和自定义 pytest 的行为。
主要钩子类别
-
初始化钩子
pytest_configure
- 配置初始化pytest_sessionstart
- 测试会话开始
-
收集钩子
pytest_collect_file
- 文件收集pytest_generate_tests
- 测试生成
-
测试运行钩子
pytest_runtest_setup
- 测试设置pytest_runtest_call
- 测试执行pytest_runtest_teardown
- 测试清理
-
报告钩子
pytest_terminal_summary
- 终端报告
钩子实现示例
def pytest_configure(config):
"""在配置阶段添加自定义行为"""
config.addinivalue_line("markers", "slow: 标记为慢速测试")
def pytest_collection_modifyitems(items):
"""修改收集到的测试项"""
for item in items:
if "slow" in item.keywords:
item.add_marker(pytest.mark.skipif(not RUN_SLOW, reason="需要显式启用慢速测试"))
高级功能
测试插件开发
使用 pytest_plugins
声明依赖:
# 在conftest.py中
pytest_plugins = ["pytester"]
缓存机制
config.cache
允许跨测试会话存储数据:
def test_with_cache(pytestconfig):
cache = pytestconfig.cache
cache.set("key", "value")
assert cache.get("key", None) == "value"
总结
pytest 提供了丰富的 API 来满足各种测试需求,从简单的断言到复杂的测试框架扩展。通过合理使用标记、夹具和钩子系统,开发者可以构建灵活、可维护的测试套件。本文介绍的核心功能只是 pytest 能力的冰山一角,实际应用中还有更多高级用法等待探索。