pin_drop当前位置:知识文库 ❯ 图文

Python pytest 框架完全指南 - 高效测试最佳实践

一、pytest 框架概述

pytest 是 Python 生态中最流行的第三方测试框架,以其简洁的语法、强大的功能和丰富的插件生态而广受开发者喜爱。与标准库的 unittest 相比,pytest 具有以下显著优势:

  • 简洁的语法:无需继承任何基类,直接编写函数即可成为测试

  • 强大的断言:使用原生 assert 语句,自动提供详细的失败信息

  • Fixture 系统:灵活的依赖注入机制,管理测试资源

  • 丰富的插件:超过800个社区插件,覆盖各种测试场景

  • 兼容 unittest:可以直接运行 unittest 编写的测试用例


二、核心语法与安装配置

1. 安装 pytest

代码示例

# 使用 pip 安装
pip install pytest

# 验证安装
pytest --version

# 安装常用插件
pip install pytest-cov pytest-xdist pytest-html

2. 命名约定

pytest 使用以下命名约定来自动发现测试:

  • 测试文件test_*.py*_test.py

  • 测试函数:以 test_ 开头的函数

  • 测试类:以 Test 开头的类(不能有 __init__ 方法)


三、基本用法与断言

1. 最简单的测试

代码示例

# test_simple.py

def add(a, b):
    return a + b

def test_add():
    """最简单的 pytest 测试"""
    assert add(2, 3) == 5
    assert add(-1, 1) == 0
    assert add(0, 0) == 0

def test_add_negative():
    """测试负数相加"""
    assert add(-2, -3) == -5

2. 强大的断言重写

pytest 最大的特点之一是使用原生的 assert 语句,并在测试失败时提供详细的上下文信息:

代码示例

def test_assert_examples():
    """pytest 断言示例"""
    # 相等断言
    assert [1, 2, 3] == [1, 2, 3]

    # 包含断言
    assert "hello" in "hello world"

    # 异常断言
    with pytest.raises(ZeroDivisionError):
        1 / 0

    # 异常信息断言
    with pytest.raises(ValueError, match="must be positive"):
        raise ValueError("value must be positive")

    # 警告断言
    with pytest.warns(DeprecationWarning):
        import warnings
        warnings.warn("deprecated", DeprecationWarning)

3. 测试类和分组

代码示例

class TestStringMethods:
    """使用测试类组织相关测试"""

    def test_upper(self):
        assert "hello".upper() == "HELLO"

    def test_isupper(self):
        assert "HELLO".isupper()
        assert not "Hello".isupper()

    def test_split(self):
        s = "hello world"
        assert s.split() == ["hello", "world"]
        with pytest.raises(TypeError):
            s.split(2)

class TestListMethods:
    """列表相关测试"""

    def test_append(self):
        lst = [1, 2]
        lst.append(3)
        assert lst == [1, 2, 3]

    def test_pop(self):
        lst = [1, 2, 3]
        assert lst.pop() == 3
        assert len(lst) == 2

4. 运行测试

代码示例

# 运行当前目录及子目录下所有测试
pytest

# 运行指定文件
pytest test_simple.py

# 运行指定测试函数
pytest test_simple.py::test_add

# 运行指定测试类中的方法
pytest test_simple.py::TestStringMethods::test_upper

# 显示详细输出
pytest -v

# 遇到第一个失败就停止
pytest -x

# 显示局部变量信息
pytest -l

# 显示测试执行时间
pytest --durations=10

四、Fixture 机制详解

Fixture 是 pytest 最强大的特性之一,用于管理测试的前置准备和后置清理工作。

代码示例

import pytest

@pytest.fixture
def sample_data():
    """最简单的 fixture"""
    return {"name": "张三", "age": 25, "city": "北京"}

def test_sample_data(sample_data):
    """通过函数参数注入 fixture"""
    assert sample_data["name"] == "张三"
    assert sample_data["age"] == 25

@pytest.fixture
def db_connection():
    """带有 setup 和 teardown 的 fixture"""
    # setup 阶段
    print("\n连接数据库...")
    connection = {"status": "connected"}

    yield connection  # 返回给测试使用

    # teardown 阶段
    print("\n断开数据库连接...")
    connection["status"] = "disconnected"

def test_db_query(db_connection):
    assert db_connection["status"] == "connected"

@pytest.fixture(scope="module")
def shared_resource():
    """module 级别的 fixture,整个模块只执行一次"""
    resource = {"data": "shared"}
    yield resource

提示:fixture 的 scope 参数可以是 function(默认)、class、module、package、session,决定了 fixture 的执行频率。


五、标记与命令行参数

1. 使用标记分组测试

代码示例

import pytest

@pytest.mark.slow
def test_long_running():
    """标记为慢速测试"""
    import time
    time.sleep(2)

@pytest.mark.skip(reason="功能尚未实现")
def test_not_implemented():
    pass

@pytest.mark.skipif(True, reason="仅在Linux运行")
def test_linux_only():
    pass

@pytest.mark.xfail(reason="已知会失败的测试")
def test_known_failure():
    assert False

# 运行特定标记的测试
# pytest -m slow          # 只运行 slow 标记的测试
# pytest -m "not slow"    # 跳过 slow 标记的测试

2. conftest.py 共享配置

conftest.py 文件用于在多个测试文件之间共享 fixture 和配置:

代码示例

# conftest.py
import pytest

@pytest.fixture
def mock_user():
    """所有测试文件都可以使用的共享 fixture"""
    return {"id": 1, "username": "testuser", "role": "admin"}

# pytest.ini 配置文件示例
# [pytest]
# markers =
#     slow: marks tests as slow
#     integration: marks tests as integration tests
# addopts = -v --tb=short

练习1

为一个用户认证模块(包含登录、注册、密码验证功能)编写完整的 pytest 测试用例,使用 fixture 来管理测试数据。

练习2

创建一个 conftest.py 文件,定义不同作用域的 fixture(function、module、session),并在测试中验证它们的执行顺序。


六、课程小结

  • 原生 assert 断言:pytest 使用原生 assert 并提供详细的失败信息

  • Fixture 依赖注入:通过函数参数传递,支持多种作用域

  • 丰富的标记系统:支持 skip、xfail、自定义标记等

  • conftest.py 共享:轻松在测试文件间共享 fixture 和配置

  • 强大的命令行:丰富的命令行参数满足不同测试需求

常见问题

pytest 和 unittest 有什么区别?

pytest 使用原生 assert 而非 unittest 的 assertEqual 等方法,语法更简洁;pytest 不需要继承基类,直接写函数即可;pytest 的 fixture 系统比 unittest 的 setUp/tearDown 更灵活;pytest 拥有丰富的插件生态。

如何在 pytest 中使用 unittest 的测试用例?

pytest 完全兼容 unittest,只需直接运行 pytest 命令即可,pytest 会自动发现并执行继承自 unittest.TestCase 的测试类,无需任何修改。

fixture 的 yield 和 return 有什么区别?

return 只返回数据给测试,yield 会将 yield 之前的代码作为 setup,yield 之后的代码作为 teardown。yield 适合需要清理资源的场景,如关闭数据库连接、删除临时文件等。

如何查看 pytest 支持的所有命令行参数?

运行 pytest --help 可以查看所有命令行参数。常用的包括 -v(详细输出)、-x(遇到失败停止)、--tb=short(简短 traceback)、-q(静默模式)、-k(按名称过滤测试)等。

标签: pytest Python测试 测试框架 Fixture 单元测试 conftest

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: Python unittest 框架完整教程 - 从入门到精通 下一篇: Python 测试夹具完全指南 - 高效管理测试资源

poll相关推荐