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

Python 测试夹具完全指南 - 高效管理测试资源

一、测试夹具概述

测试夹具(Test Fixture)是测试框架中用于管理测试前置准备和后置清理的机制。它的核心作用是确保每个测试都在一致、干净的环境中执行,避免测试之间的相互影响。

常见的测试夹具场景包括:

  • 数据库连接:测试前创建连接,测试后关闭连接

  • 临时文件:测试前创建临时文件,测试后删除

  • 测试数据:提供标准化的测试数据集

  • Mock 服务:模拟外部 API 或第三方服务

  • Web 客户端:测试前初始化 HTTP 客户端或浏览器实例


二、unittest 中的 setUp/tearDown

1. 方法级别的夹具

代码示例

import unittest

class TestDatabase(unittest.TestCase):

    def setUp(self):
        """每个测试方法执行前调用"""
        self.db = {
            "connection": "connected",
            "tables": {}
        }
        print(f"\nsetUp: 初始化数据库连接")

    def tearDown(self):
        """每个测试方法执行后调用"""
        self.db["connection"] = "disconnected"
        print(f"tearDown: 关闭数据库连接")

    def test_create_table(self):
        self.db["tables"]["users"] = []
        self.assertIn("users", self.db["tables"])

    def test_insert_data(self):
        self.db["tables"]["users"] = [{"id": 1, "name": "张三"}]
        self.assertEqual(len(self.db["tables"]["users"]), 1)

2. 类级别的夹具

代码示例

import unittest

class TestSharedResource(unittest.TestCase):
    """类级别夹具 - 整个测试类只执行一次"""

    @classmethod
    def setUpClass(cls):
        """测试类开始前执行一次"""
        cls.shared_resource = {"data": "shared across all tests"}
        print("\nsetUpClass: 初始化共享资源")

    @classmethod
    def tearDownClass(cls):
        """测试类结束后执行一次"""
        cls.shared_resource = None
        print("tearDownClass: 清理共享资源")

    def test_one(self):
        self.assertEqual(self.shared_resource["data"], "shared across all tests")

    def test_two(self):
        self.assertIn("data", self.shared_resource)

3. 执行顺序

代码示例

# unittest 夹具执行顺序:
# setUpClass()       → 整个类执行前(1次)
#   setUp()          → 每个测试方法前(N次)
#     test_method()  → 测试方法本身
#   tearDown()       → 每个测试方法后(N次)
# tearDownClass()    → 整个类执行后(1次)

三、pytest 的 Fixture 系统

1. 基本 Fixture

代码示例

import pytest

@pytest.fixture
def sample_user():
    """最简单的 fixture - 返回测试数据"""
    return {
        "username": "testuser",
        "email": "test@example.com",
        "role": "user"
    }

def test_user_data(sample_user):
    """通过参数名自动注入 fixture"""
    assert sample_user["username"] == "testuser"
    assert "@" in sample_user["email"]

2. 带 Setup 和 Teardown 的 Fixture

代码示例

import pytest

@pytest.fixture
def database():
    """使用 yield 实现 setup 和 teardown"""
    # setup 阶段
    print("\n[Setup] 创建数据库连接")
    db = {
        "connection": True,
        "data": {}
    }

    # yield 给测试使用
    yield db

    # teardown 阶段
    print("[Teardown] 关闭数据库连接")
    db["connection"] = False

def test_insert(database):
    database["data"]["key1"] = "value1"
    assert database["connection"] is True
    assert database["data"]["key1"] == "value1"

3. Fixture 依赖其他 Fixture

代码示例

import pytest

@pytest.fixture
def db_connection():
    """数据库连接 fixture"""
    conn = {"status": "connected"}
    yield conn
    conn["status"] = "disconnected"

@pytest.fixture
def user_table(db_connection):
    """依赖 db_connection 的 fixture"""
    assert db_connection["status"] == "connected"
    table = {"name": "users", "rows": []}
    return table

def test_insert_user(user_table):
    user_table["rows"].append({"id": 1, "name": "张三"})
    assert len(user_table["rows"]) == 1

四、Fixture 作用域详解

pytest 的 fixture 支持5种作用域,控制 fixture 的执行频率:

作用域 执行频率 适用场景
function 每个测试函数执行一次 默认值,测试数据准备
class 每个测试类执行一次 类级别的共享资源
module 每个测试模块执行一次 模块级别的初始化
package 每个测试包执行一次 包级别的共享配置
session 整个测试会话执行一次 全局资源,如数据库连接池

代码示例

import pytest

@pytest.fixture(scope="function")
def func_fixture():
    """每个测试函数执行一次(默认)"""
    print("\n[function] setup")
    yield
    print("[function] teardown")

@pytest.fixture(scope="class")
def class_fixture():
    """每个测试类执行一次"""
    print("\n[class] setup")
    yield
    print("[class] teardown")

@pytest.fixture(scope="module")
def module_fixture():
    """每个测试模块执行一次"""
    print("\n[module] setup")
    yield
    print("[module] teardown")

@pytest.fixture(scope="session")
def session_fixture():
    """整个测试会话只执行一次"""
    print("\n[session] setup")
    yield
    print("[session] teardown")

class TestScopes:
    def test_1(self, func_fixture, class_fixture, module_fixture, session_fixture):
        assert True

    def test_2(self, func_fixture, class_fixture, module_fixture, session_fixture):
        assert True

提示:选择合适的作用域可以显著提升测试性能。对于耗时的资源初始化(如数据库连接),使用 session 或 module 级别避免重复创建。


五、高级用法与实战技巧

1. conftest.py 共享 Fixture

代码示例

# conftest.py
import pytest

@pytest.fixture
def mock_api_client():
    """所有测试文件都可以使用的共享 fixture"""
    class MockAPIClient:
        def get(self, url):
            return {"status": 200, "data": {}}

        def post(self, url, data):
            return {"status": 201, "id": 1}

    return MockAPIClient()

# tests/test_user.py
# 无需任何导入,直接使用 mock_api_client
def test_get_user(mock_api_client):
    response = mock_api_client.get("/users/1")
    assert response["status"] == 200

2. 参数化 Fixture

代码示例

import pytest

@pytest.fixture(params=["sqlite", "postgresql", "mysql"])
def db_type(request):
    """参数化 fixture,会自动运行多次"""
    return request.param

def test_database_connection(db_type):
    """这个测试会执行3次,每次使用不同的数据库类型"""
    print(f"\n测试连接 {db_type}")
    assert db_type in ["sqlite", "postgresql", "mysql"]

3. 自动使用 Fixture

代码示例

import pytest

@pytest.fixture(autouse=True)
def log_test_execution(request):
    """自动应用的 fixture,无需在测试函数中声明"""
    print(f"\n开始执行: {request.node.name}")
    yield
    print(f"执行完成: {request.node.name}")

def test_auto_1():
    # log_test_execution 自动执行
    assert True

def test_auto_2():
    # log_test_execution 自动执行
    assert True

练习1

创建一个临时目录管理的 fixture,在每个测试前创建临时目录,测试后自动清理。使用 pytest 的 tmp_path 内置 fixture 实现。

练习2

为一个 Web 应用测试编写夹具系统,包含:数据库连接(session级别)、认证用户(function级别)、测试客户端(function级别),并在测试中组合使用它们。


六、课程小结

  • 夹具保证测试独立性:每个测试在干净、一致的环境中执行

  • unittest 使用 setUp/tearDown:方法级别和类级别的夹具机制

  • pytest Fixture 更灵活:通过依赖注入,支持 yield 实现 setup/teardown

  • 作用域控制执行频率:function/class/module/package/session 五种级别

  • conftest.py 共享配置:在测试文件间复用 fixture

常见问题

fixture 的 scope 选择有什么原则?

优先使用最小作用域(function)保证测试独立性。对于创建成本高的资源(数据库连接、浏览器实例),可以考虑 module 或 session 级别。但要注意高作用域的 fixture 可能导致测试间状态污染。

如何在 fixture 中处理异常?

在 yield 前使用 try/finally 确保资源被正确清理。pytest 也支持在 fixture 中使用 addfinalizer 注册清理函数,即使 setup 阶段抛出异常也会被调用。

autouse=True 的 fixture 有什么使用场景?

autouse 适用于全局性的操作,如测试日志记录、性能计时、数据库事务回滚等。它会在匹配范围内的每个测试自动执行,无需手动声明。

pytest 有哪些内置的 fixture?

常用内置 fixture 包括:tmp_path(临时目录)、tmpdir(旧版临时目录)、capfd(捕获标准输出)、monkeypatch(修改环境变量)、pytestconfig(访问配置)等。运行 pytest --fixtures 可查看所有可用 fixture。

标签: Fixture setUp tearDown 测试夹具 conftest 作用域

本文涉及AI创作

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

list快速访问

上一篇: Python pytest 框架完全指南 - 高效测试最佳实践 下一篇: Python 参数化测试完整教程 - 数据驱动高效测试

poll相关推荐