首页
/ pytest API 参考指南:从核心功能到高级用法详解

pytest API 参考指南:从核心功能到高级用法详解

2025-07-06 03:42:34作者:曹令琨Iris

概述

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

核心功能函数

测试控制函数

  1. pytest.fail() - 强制使测试失败

    def test_something():
        if not condition:
            pytest.fail("明确说明失败原因")
    
  2. pytest.skip() - 跳过当前测试

    def test_requires_special_env():
        if not has_special_env():
            pytest.skip("需要特殊环境才能运行")
    
  3. pytest.xfail() - 标记预期失败的测试

    def test_experimental_feature():
        pytest.xfail("此功能尚未完善")
    
  4. pytest.exit() - 终止测试会话

    if critical_error:
        pytest.exit("关键错误,终止所有测试")
    

断言与异常处理

  1. pytest.raises() - 验证代码是否抛出预期异常

    def test_division_by_zero():
        with pytest.raises(ZeroDivisionError):
            1 / 0
    
  2. pytest.approx() - 浮点数近似比较

    def test_floating_point():
        assert 0.1 + 0.2 == pytest.approx(0.3)
    

其他实用函数

  1. pytest.importorskip() - 条件导入模块

    numpy = pytest.importorskip("numpy")
    
  2. pytest.param() - 参数化测试中的参数封装

    @pytest.mark.parametrize("x", [pytest.param(1, id="case1")])
    def test(x):
        pass
    

标记(Mark)系统

pytest 的标记系统允许为测试函数添加元数据,这些标记可以被夹具或插件访问。

内置标记

  1. @pytest.mark.skip - 无条件跳过测试

    @pytest.mark.skip(reason="尚未实现")
    def test_feature():
        pass
    
  2. @pytest.mark.skipif - 条件跳过测试

    @pytest.mark.skipif(sys.version_info < (3, 8), reason="需要Python 3.8+")
    def test_new_syntax():
        pass
    
  3. @pytest.mark.parametrize - 参数化测试

    @pytest.mark.parametrize("input,expected", [(1, 2), (3, 4)])
    def test_increment(input, expected):
        assert input + 1 == expected
    
  4. @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

内置夹具

  1. tmp_path - 临时目录路径(Path对象)

    def test_create_file(tmp_path):
        file = tmp_path / "test.txt"
        file.write_text("content")
        assert file.read_text() == "content"
    
  2. monkeypatch - 临时修改环境

    def test_env_var(monkeypatch):
        monkeypatch.setenv("DEBUG", "1")
        assert os.getenv("DEBUG") == "1"
    
  3. capsys - 捕获标准输出/错误

    def test_print(capsys):
        print("hello")
        captured = capsys.readouterr()
        assert captured.out == "hello\n"
    
  4. caplog - 捕获日志

    def test_logging(caplog):
        logging.warning("test message")
        assert "test message" in caplog.text
    

钩子(Hook)系统

钩子系统允许开发者扩展和自定义 pytest 的行为。

主要钩子类别

  1. 初始化钩子

    • pytest_configure - 配置初始化
    • pytest_sessionstart - 测试会话开始
  2. 收集钩子

    • pytest_collect_file - 文件收集
    • pytest_generate_tests - 测试生成
  3. 测试运行钩子

    • pytest_runtest_setup - 测试设置
    • pytest_runtest_call - 测试执行
    • pytest_runtest_teardown - 测试清理
  4. 报告钩子

    • 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 能力的冰山一角,实际应用中还有更多高级用法等待探索。