You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在多个测试用例间共享函数作用域fixture初始化的数据

Pytest中为只读测试共享高开销数据库Fixture的解决方案

问题背景

你有一个开销极大的数据库初始化Fixture(创建表、插入数据),因常规测试会修改数据状态,所以设为函数作用域。但存在一批只读查询测试,可共享同一数据库状态以提升效率,现有方案均存在缺陷:

  • pytest.mark.parametrize会重复执行Fixture,效率无提升;
  • 改为模块作用域会与pytest-asyncio的函数作用域事件循环冲突,且无法仅针对这批测试切换作用域;
  • 单测试函数内循环执行无法定位失败用例,也不便单独运行。

解决方案一:为只读测试创建专属模块作用域Fixture

针对只读测试单独维护一套模块作用域的Fixture,既保证状态共享,又不影响常规测试的函数作用域Fixture。

实现步骤

  1. 将所有只读测试集中到一个独立模块(如test_read_only_queries.py);
  2. 在该模块内定义模块作用域的数据库Fixture,配合pytest-asyncio的异步Fixture支持:
import pytest
import pytest_asyncio
from your_db_module import init_database, get_db_connection

@pytest_asyncio.fixture(scope="module")
async def module_scoped_db():
    # 仅初始化一次数据库
    await init_database()
    db_conn = await get_db_connection()
    yield db_conn
    # 只读测试若无需清理可省略此步骤
    await db_conn.close()

# 只读测试使用该模块作用域Fixture
@pytest.mark.asyncio
async def test_user_list_query(module_scoped_db):
    result = await module_scoped_db.fetch("SELECT * FROM users")
    assert len(result) > 0

@pytest.mark.asyncio
async def test_product_count_query(module_scoped_db):
    result = await module_scoped_db.fetchval("SELECT COUNT(*) FROM products")
    assert result == 100

注意事项

  • 虽然pytest-asyncio官方不建议将事件循环改为模块作用域,但只读测试无状态修改,不会引发并发安全问题,风险可控;
  • 该Fixture仅作用于当前模块,不会影响其他测试文件的函数作用域Fixture。

解决方案二:使用pytest-subtests将循环用例转为独立子测试

保持原函数作用域Fixture不变,通过pytest-subtests插件将循环内的测试逻辑转为pytest可识别的独立子测试,既共享Fixture实例,又能单独定位失败用例。

实现步骤

  1. 安装插件:
pip install pytest-subtests
  1. 编写测试函数,用subtests.test()包裹每个只读查询的测试逻辑:
import pytest
from your_db_module import init_database, get_db_connection, read_query_list

@pytest.fixture(scope="function")
async def db():
    await init_database()
    db_conn = await get_db_connection()
    yield db_conn
    await db_conn.close()

@pytest.mark.asyncio
async def test_all_read_queries(db, subtests):
    # read_query_list为包含查询名称和执行逻辑的字典/列表
    for query_name, query_func in read_query_list.items():
        with subtests.test(msg=query_name):
            result = await query_func(db)
            # 针对该查询的断言逻辑
            assert result is not None
            assert len(result) > 0

效果说明

  • 运行测试时,每个子测试会被pytest当作独立条目展示,失败时会明确标记对应的查询名称;
  • 可通过pytest -k "查询名称"单独运行指定的子测试;
  • Fixture仅执行一次(因为所有子测试在同一个测试函数内),达到状态共享的目的。

内容的提问来源于stack exchange,提问作者Adam Budziak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 20:25:59