Files
2026-07-13 12:29:33 +08:00

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_ 开头。
  • 通常不接受参数(除非使用 fixtureparametrize)。
  • 使用 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 插件)