
Pytest 默认对相同参数的 fixture 实例进行缓存,导致 @pytest.mark.parametrize 中重复参数仅触发一次 fixture 执行。本文介绍通过将 fixture 改写为工厂函数(factory fixture)的方式,实现每次调用都独立执行 DynamoDB 数据插入逻辑,从而准确模拟订单数量超限场景。
pytest 默认对相同参数的 fixture 实例进行缓存,导致 `@pytest.mark.parametrize` 中重复参数仅触发一次 fixture 执行。本文介绍通过将 fixture 改写为工厂函数(factory fixture)的方式,实现每次调用都独立执行 dynamodb 数据插入逻辑,从而准确模拟订单数量超限场景。
在 Pytest 中,fixture 的执行次数与参数化行为密切相关。当使用 indirect=True 对 fixture 进行参数化时(如 @pytest.mark.parametrize("function_order", [...], indirect=True)),Pytest 会根据参数值的唯一性决定是否复用已缓存的 fixture 实例。这意味着:若两个参数字典完全相同(例如 {"delivery_date": "12-12-2024"} 出现两次),Pytest 将仅运行该 fixture 一次,并在两次测试调用中复用其返回值——这显然不符合集成测试中“需真实插入两条同日期订单”的预期。
根本原因在于:Pytest 的 fixture 缓存机制基于参数哈希(而非调用次数),且 scope="function" 仅保证每个测试函数内不重复执行,无法突破参数去重逻辑。
✅ 正确解法是将 fixture 重构为返回可调用工厂函数(即 factory fixture),而非直接执行副作用操作:
import uuid
import pytest
@pytest.fixture(scope="function")
def function_order(request, dynamodb_client, cleanup_orders):
"""Factory fixture: returns a callable that inserts one order per call."""
def _insert_order(delivery_date="01-01-2022"):
order_id = f"ORDER-{uuid.uuid4()}"
record = {
"order_id": {"S": order_id},
"delivery_date": {"S": delivery_date},
}
dynamodb_client.put_item(
TableName="orders",
Item=record,
)
cleanup_orders.append(order_id)
return {"order_id": order_id, "record": record}
return _insert_order
随后,在测试中显式调用该工厂函数多次,确保每次调用都独立完成 DynamoDB 写入:
def test_post_v1_orders_exceeds_order_limit_returns_400(
request_helper, function_order
):
delivery_date = "12-12-2024"
# 插入前两笔同日期订单(两次独立调用 → 两次 DynamoDB put_item)
function_order(delivery_date)
function_order(delivery_date)
# 第三笔请求应触发 400 错误
response = request_helper.post(
"/v1/orders",
body=order.order_record(delivery_date=delivery_date),
)
assert response.status_code == 400
assert response.json().get("error") == f"Order limit exceeded for date: {delivery_date}. Max orders: 2"
? 关键优势:
- ✅ 完全绕过 Pytest 参数化缓存限制;
- ✅ 每次调用
function_order(...)都生成新order_id并执行真实 DB 操作; - ✅ 清晰表达测试意图:「先准备 N 条数据,再验证第 N+1 条的行为」;
- ✅ 保持 fixture 的 scope 和依赖注入能力(如
dynamodb_client、cleanup_orders仍由 Pytest 管理)。
⚠️ 注意事项:
- 不要在 factory fixture 内部做
yield或复杂状态管理,避免隐式共享; -
cleanup_orders需确保在测试结束后正确清理所有追加的order_id(建议在function_orderfixture 外层或autousefixture 中统一处理); - 若需支持更灵活的参数(如批量插入、自定义字段),可在
_insert_order中扩展参数签名。
这种工厂模式是 Pytest 中处理「需多次触发副作用」场景的最佳实践,兼顾可读性、可维护性与测试准确性。










