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