Pytest 框架详细使用指南

一、 Pytest 是什么?为什么选择它?

Pytest 是一个基于 Python 的成熟、功能丰富的测试框架。它几乎可以用于所有类型和级别的软件测试。

核心优势:

  1. 简单易上手:用例编写简单,断言使用标准的 assert 语句,无需学习更多 API。

  2. 功能强大:支持参数化、Fixture(夹具)、重运行失败用例、分布式执行等高级功能。

  3. 丰富的插件生态:拥有庞大的插件系统(如 pytest-html 生成报告,pytest-xdist 并行执行,pytest-rerunfailures 重试失败用例等)。

  4. 兼容性好:可以运行 unittest, nose 等传统测试框架的用例。


二、 环境搭建与安装

在开始之前,请确保你的系统已安装 Python。然后通过 pip 安装 Pytest。

bash

# 安装 pytest
pip install pytest

# 验证安装,查看版本
pytest --version

三、 第一个测试用例:从编写到运行

1. 创建项目结构
创建一个清晰的目录结构是良好实践的开始。

text

my_pytest_project/
├── tests/          # 专门存放测试用例的目录
│   └── test_sample.py
└── README.md

2. 编写第一个测试用例
在 tests/test_sample.py 中编写以下代码:

python

# 待测试的函数(通常放在另一个文件,这里为了演示方便)
def add(a, b):
    return a + b

# Pytest 测试用例
# 规则1:测试文件以 `test_` 开头或结尾
# 规则2:测试类以 `Test` 开头
# 规则3:测试函数/方法以 `test_` 开头
def test_add():
    # 使用简单的 assert 语句进行断言
    assert add(1, 2) == 3
    assert add(-1, 1) == 0
    assert add(0, 0) == 0

# 一个预期会失败的用例
def test_add_fail():
    assert add(1, 1) == 3  # 这个断言会失败

3. 运行测试
在项目根目录 my_pytest_project/ 下打开终端/命令行。

bash

# 最基本运行方式:运行当前目录及子目录所有 test_*.py 文件里的所有 test_* 函数
pytest

# 运行指定文件
pytest tests/test_sample.py

# 运行指定文件中的指定用例(通过函数名匹配)
pytest tests/test_sample.py::test_add

#  verbose 模式,输出更详细信息
pytest -v

# 运行后,你会看到输出结果,清晰地显示通过、失败、错误的状态。

运行 pytest -v 后,你将看到类似如下输出:

text

========================= test session starts =========================
collected 2 items

tests/test_sample.py::test_add PASSED                           [ 50%]
tests/test_sample.py::test_add_fail FAILED                      [100%]

============================== FAILURES ===============================
_____________________________ test_add_fail ____________________________

    def test_add_fail():
>       assert add(1, 1) == 3  # 这个断言会失败
E       assert 2 == 3
E        +  where 2 = add(1, 1)

tests/test_sample.py:15: AssertionError
======================= short test summary info =======================
FAILED tests/test_sample.py::test_add_fail - assert 2 == 3
===================== 1 failed, 1 passed in 0.10s =====================

四、 核心功能详解与实战

1. Fixture(夹具):测试的“基石”
Fixture 是 Pytest 最核心的功能。它用于为测试用例提供预置环境(如数据库连接、测试数据)和清理工作。

  • 使用 @pytest.fixture 装饰器定义

  • 通过在测试函数参数列表中声明夹具名称来使用

python

# tests/test_fixture.py
import pytest

# 定义一个简单的 Fixture,返回一个列表
@pytest.fixture
def my_list():
    print("\n(Setting up the list fixture)")  #  setup 部分
    yield [1, 2, 3]  # 这是测试用例实际拿到的值
    print("\n(Tearing down the list fixture)") # 可选的 teardown 部分

# 使用 Fixture:只需将夹具函数名作为参数传入
def test_list_length(my_list):
    assert len(my_list) == 3

def test_list_sum(my_list):
    assert sum(my_list) == 6

运行 pytest -v -s tests/test_fixture.py (-s 用于显示 print 语句的输出)。

  • autouse 参数:让夹具自动执行,无需显示声明。

    python

    @pytest.fixture(autouse=True)
    def auto_fixture():
        print("\nThis runs automatically for every test in this module!")
  • scope 参数:控制夹具的作用域(function(默认), classmodulepackagesession)。

    python

    @pytest.fixture(scope="module") # 整个模块只执行一次
    def db_connection():
        conn = create_expensive_connection()
        yield conn
        conn.close()
  • conftest.py:一个神奇的配置文件,用于存放全局共享的 Fixture。Pytest 会自动发现它。

    • 在项目根目录或任何子目录创建 conftest.py

    • 将 Fixture 移入其中,所有同级及子目录的测试文件均可直接使用,无需导入。

2. 参数化测试:一个用例,多组数据
使用 @pytest.mark.parametrize 装饰器,避免写大量重复代码来测试不同输入。

python

# tests/test_parametrize.py
import pytest

# 待测试函数
def is_even(n):
    return n % 2 == 0

# 参数化:第一个参数是字符串,表示参数名;第二个参数是数据列表
@pytest.mark.parametrize("number, expected", [
    (2, True),
    (1, False),
    (0, True),
    (-1, False),
    (-2, True),
])
def test_is_even(number, expected):
    assert is_even(number) == expected

运行后,Pytest 会将这组数据展开成 5 个独立的测试用例执行。

3. Mark(标记):对测试用例进行分类
Mark 可以用来做跳过(skip)、预期失败(xfail)、自定义分组等。

python

# tests/test_mark.py
import pytest
import sys

@pytest.mark.skip(reason="This feature is broken, skipping for now.")
def test_skipped():
    assert False

@pytest.mark.skipif(sys.version_info < (3, 8), reason="Requires Python 3.8+")
def test_skipif():
    assert True

# 预期会失败,如果测试通过了,报告为 XPASS;失败了,报告为 XFAIL
@pytest.mark.xfail
def test_expected_to_fail():
    assert False

# 自定义标记,比如标记为“慢测试”
@pytest.mark.slow
def test_slow_integration():
    # ... some slow code ...
    assert True

运行特定标记的用例:pytest -v -m slow tests/test_mark.py

4. 断言细节:Pytest 会告诉你为什么失败
Pytest 重写了断言,提供了极其清晰的失败信息。对于复杂数据结构(如列表、字典)的对比,效果尤其明显。

python

def test_complex_assertion():
    actual_result = {"name": "Alice", "age": 30, "hobbies": ["coding", "hiking"]}
    expected_result = {"name": "Bob", "age": 30, "hobbies": ["reading", "hiking"]}

    assert actual_result == expected_result

运行这个用例,Pytest 会高亮显示字典中不匹配的键值对,非常利于调试。


五、 实战:搭建一个简单的 Web UI 自动化测试(以 Selenium 为例)

让我们把上面的知识用起来,模拟一个真实场景。

1. 安装依赖

bash

pip install pytest selenium
# 记得下载对应的浏览器驱动(如 chromedriver)并放到 PATH 下

2. 项目结构

text

my_web_test_project/
├── conftest.py        # 全局 Fixture
├── tests/
│   ├── __init__.py
│   └── test_google_search.py
└── requirements.txt

3. 编写 conftest.py(共享浏览器驱动)

python

# conftest.py
import pytest
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

@pytest.fixture(scope="session") # 整个测试会话只启动一次浏览器
def browser():
    options = Options()
    options.headless = True  # 无头模式,不打开浏览器UI
    driver = webdriver.Chrome(options=options)
    driver.implicitly_wait(10)
    yield driver
    # Teardown:所有测试结束后,关闭浏览器
    driver.quit()

4. 编写测试用例 test_google_search.py

python

# tests/test_google_search.py
import pytest

# 使用 conftest.py 中定义的 browser fixture
def test_google_search_title(browser):
    browser.get("https://www.google.com")
    assert "Google" in browser.title

# 参数化搜索关键词
@pytest.mark.parametrize("keyword", ["pytest", "selenium", "python"])
def test_google_search(browser, keyword):
    browser.get("https://www.google.com")
    search_box = browser.find_element_by_name("q")
    search_box.send_keys(keyword)
    search_box.submit()

    # 简单断言搜索结果页面标题包含关键词
    assert keyword in browser.title

5. 运行并生成报告

bash

# 运行所有测试
pytest -v

# 安装 html 报告插件后运行
pip install pytest-html
pytest --html=report.html

六、 常用配置与命令
  • 配置文件 pytest.ini:放在项目根目录,用于配置默认行为。

    ini

    # pytest.ini
    [pytest]
    addopts = -v --html=report.html  # 默认命令行参数
    testpaths = tests               # 测试目录
    markers =
        slow: marks tests as slow (deselect with '-m "not slow"')
  • 常用命令

    • pytest -k "add":运行名称中包含 "add" 的用例。

    • pytest --maxfail=2:失败2个用例后停止测试。

    • pytest -x:遇到第一个失败就停止。

    • pytest --tb=short:简化错误回溯信息。

总结与下一步

恭喜!你现在已经掌握了 Pytest 的核心概念:

  1. 基础:用例编写规则、断言、运行命令。

  2. 核心Fixtureconftest.pyscopeautouse)和参数化

  3. 进阶:Mark 标记、插件系统、配置文件。

给你的学习建议:

  1. 动手:把上面的例子全部敲一遍,并确保能运行成功。

  2. 改造:尝试用 Pytest 去改写你之前用 unittest 或自己写的测试脚本。

  3. 探索插件:尝试使用 pytest-htmlpytest-xdist(并行测试), pytest-rerunfailures(失败重试)等常用插件。

  4. 集成:学习如何将 Pytest 集成到 CI/CD 工具(如 Jenkins, GitLab CI)中。

Pytest 的生态系统非常丰富,官方文档是最好的朋友:pytest documentation

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐