Fixture 指南

基础 Fixture 与作用域

import pytest # function 作用域(默认)— 每个测试创建新实例 @pytest.fixture def db_connection(): conn = create_connection("sqlite:///:memory:") yield conn conn.close() # module 作用域 — 同文件内共享 @pytest.fixture(scope="module") def http_client(): return TestClient(app) # session 作用域 — 整个测试会话共享 @pytest.fixture(scope="session") def app_config(): return load_config("test.yaml") # 作用域优先级(从宽到窄): # session > package > module > class > function

conftest.py — 共享 Fixtures

# conftest.py(放在项目根目录或测试目录) import pytest from myapp import create_app, db as _db @pytest.fixture(scope="session") def app(): app = create_app({"TESTING": True, "DATABASE_URL": "sqlite:///:memory:"}) return app @pytest.fixture(scope="session") def db(app): with app.app_context(): _db.create_all() yield _db _db.drop_all() @pytest.fixture def client(app): return app.test_client() @pytest.fixture def auth_headers(): token = generate_test_token(user_id=1, role="admin") return {"Authorization": f"Bearer {token}"} # conftest.py 被 pytest 自动发现,无需在测试文件中导入

yield Fixtures — 初始化与清理

import pytest from unittest.mock import patch @pytest.fixture def mock_external_api(): with patch("myapp.services.requests.get") as mock_get: mock_get.return_value.json.return_value = {"status": "ok"} mock_get.return_value.status_code = 200 yield mock_get # yield 之后的代码自动作为清理逻辑执行 @pytest.fixture def temp_user(db): user = User(name="测试用户", email="[email protected]") db.session.add(user) db.session.commit() yield user db.session.delete(user) db.session.commit()

参数化 Fixtures

import pytest @pytest.fixture(params=["sqlite", "postgres", "mysql"]) def database(request): """针对多个数据库后端运行测试。""" db_type = request.param conn = connect_to_db(db_type) yield conn conn.close() def test_insert_record(database): # 该测试运行 3 次,每种数据库各一次 database.execute("INSERT INTO users (name) VALUES ('张三')") result = database.execute("SELECT * FROM users").fetchall() assert len(result) == 1

autouse Fixtures

import pytest # 自动应用于作用域内所有测试 @pytest.fixture(autouse=True) def reset_database(db): yield db.session.rollback() # 类级别 autouse class TestPayments: @pytest.fixture(autouse=True) def setup_payment_gateway(self): with patch("myapp.payments.stripe") as mock_stripe: mock_stripe.charge.return_value = {"id": "ch_test"} self.stripe = mock_stripe yield

tmpdir / tmp_path — 临时文件

import pytest # tmp_path(现代写法,pathlib.Path,推荐) def test_writes_output_file(tmp_path): output_file = tmp_path / "output.txt" write_report(output_file) assert output_file.exists() assert output_file.read_text() == "报告完成\n" # tmp_path_factory 用于 session 作用域临时目录 @pytest.fixture(scope="session") def shared_data_dir(tmp_path_factory): base = tmp_path_factory.mktemp("data") (base / "sample.csv").write_text("id,name\n1,张三\n") return base