pytest Fixtures 详解:测试夹具的创建、使用与作用域

小飞兽 Python 8 次阅读 2026-07-24

Introduction

写自动化测试时,经常遇到这样的问题:每个测试都要连接数据库、创建临时文件,但这些准备工作重复写在每个测试函数里会非常冗余。

pytest Fixtures(测试夹具)就是解决这个问题的——你定义一次,pytest 自动在测试前后执行,还能精确控制作用域(function、class、module、session)。

---

基础语法

1. 最简单的 Fixture

import pytest

@pytest.fixture
def sample_data():
    return {"name": "pytest", "version": "8.0"}

作为参数传入测试函数即可使用:

def test_data_access(sample_data):
    assert sample_data["name"] == "pytest"
    assert sample_data["version"] == "8.0"

2. Setup 和 Teardown

用 yield 实现「测试前准备 + 测试后清理」:

import pytest
import os

@pytest.fixture
def temp_dir():
    """创建临时目录,测试后清理"""
    path = "/tmp/pytest_test_dir"
    os.makedirs(path, exist_ok=True)
    yield path  # 测试在此处执行
    # teardown:测试结束后执行
    import shutil
    if os.path.exists(path):
        shutil.rmtree(path)

def test_create_file(temp_dir):
    filepath = os.path.join(temp_dir, "test.txt")
    with open(filepath, "w") as f:
        f.write("hello")
    assert os.path.exists(filepath)
    assert os.path.getsize(filepath) == 5

3. Fixture 作用域(Scope)

| scope | 说明 | 何时创建 | 何时销毁 |
|-------|------|----------|----------|
| function(默认) | 每个测试函数执行一次 | 第一次使用时 | 测试结束后 |
| class | 每个测试类执行一次 | 第一次使用时 | 类最后一个测试结束后 |
| module | 每个 .py 文件执行一次 | 文件首次使用时 | 模块所有测试结束后 |
| session | 整个测试会话执行一次 | 会话开始时 | 会话结束时 |

@pytest.fixture(scope="module")
def db_connection():
    """整个模块只建立一次数据库连接"""
    conn = create_db_connection()
    yield conn
    conn.close()

class TestUserAPI:
    def test_get_user(self, db_connection):
        ...

    def test_create_user(self, db_connection):
        ...

4. autouse:自动调用

不需要显式传入,某些 fixture 可以设置为自动执行:

@pytest.fixture(autouse=True)
def test_env_setup():
    """每个测试函数自动执行,不需要手动传入"""
    os.environ["TESTING"] = "1"
    yield
    os.environ.pop("TESTING", None)

5. Fixture 互相依赖

Fixture 之间可以相互引用:

@pytest.fixture
def database():
    return DatabaseConnection()

@pytest.fixture
def user_repo(database):
    return UserRepository(database)

def test_create_user(user_repo):
    user = user_repo.create(name="pytest")
    assert user.name == "pytest"

---

运行效果

pytest test_fixtures.py -v

# output:
# test_fixtures.py::test_create_file PASSED  [50%]
# test_fixtures.py::test_data_access PASSED   [50%]

---

常见问题

Q1: fixture 和 setup/teardown 有什么区别?

@pytest.fixture + yield = setup + teardown,能返回值给测试函数;setup/teardown 是类/函数级别的钩子,不能返回值。

Q2: fixture 报 "fixture not found"?

检查是否: 1. fixture 和测试函数在同一个文件 2. fixture 写在 conftest.py 中(推荐,模块间共享) 3. 参数名称与 fixture 名称完全一致

Q3: conftest.py 怎么用?

放在测试目录根部,所有子目录的测试都能自动使用其中的 fixtures:
tests/
├── conftest.py          ← fixtures 放这里
├── test_api.py
└── test_unit/
    └── test_sample.py

---

延伸阅读

  • [[pytest 参数化测试]] — 多组数据驱动
  • [[pytest 插件生态]] — pytest-cov、pytest-html、pytest-xdist
  • [[Selenium + pytest 集成]] — Web UI 测试实战

---
有问题欢迎在评论区交流!