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

如何阻止python-hypothesis重试产生异常Interesting结果丢失原始报错

问题原因

你配置错参数了:report_multiple_bugs=False 仅用于控制用例收缩阶段遇到多个不同类型bug时是否批量上报,完全不会关闭Hypothesis内置的「失败用例重跑验证」逻辑——只要Hypothesis检测到一次失败,默认至少会重跑1次对应用例,确认失败可稳定复现,之后才会进入用例收缩阶段找最小复现输入。
你现在把数据库初始化、清理逻辑全写在测试函数体内,只要某一次执行时add_column或之前的步骤抛异常,后面的await end_database(database)根本不会执行,残留的库表、连接、测试数据会直接污染后续所有重跑、收缩阶段的执行环境,才会出现第一次报PostgresSyntaxError、重跑时报InternalServerError的Flaky错误。

解决方法

按优先级从高到低选择:

1. 用pytest fixture做强制资源清理(根治方案)

把数据库初始化、清理逻辑挪到函数级fixture里,pytest会保证fixture的清理逻辑无论测试成功、抛错、中途中断都一定会执行,从根源避免状态污染。示例写法:

import pytest

@pytest.fixture
async def test_database():
    # 初始化逻辑
    db = await get_database()
    yield db
    # 清理逻辑:任何场景下都会执行
    await end_database(db)
    # 保险起见可以在这里加硬删除逻辑,直接删掉本次测试创建的所有project/source相关库表

测试代码改成直接注入fixture拿数据库实例,不要自己在测试体内写初始化/清理:

@given(st.text(min_size=1, max_size=128))
@pytest.mark.asyncio
async def test_hypothesis_add_column(test_database, name):
    assume('\x00' not in name)
    project = await test_database.create_project('test_project')
    source = await project.create_source(
        'test_source',
        [
            lib.ColumnDefinition(
                name='external_id',
                type=lib.ColumnTypes.NUMERIC,
                is_key=True
            )
        ]
    )
    await source.add_column(lib.ColumnDefinition(
        name=name,
        type=lib.ColumnTypes.TEXT
    ))
    assert len(source.columns) == 2
    assert await source.column(name) is not None
    assert (await source.column(name)).internal_name.isidentifier()

注意fixture的作用域必须是默认的function级,这样Hypothesis每一次执行测试(包括生成用例、重跑验证、收缩最小用例的所有尝试)都会拿到全新的干净环境,不会出现状态污染。

2. 临时开详细日志拿首次报错

如果你暂时不想改fixture逻辑,只想先拿到第一次触发PostgresSyntaxError的输入和栈,直接给Hypothesis开verbose级别的日志,它会打印每一次执行的输入、对应异常,哪怕后续重跑出别的错,你也能从控制台日志里翻到首次失败的完整信息:

from hypothesis import settings, Verbosity

@given(st.text(min_size=1, max_size=128))
@settings(
    verbosity=Verbosity.verbose,
    report_multiple_bugs=False
)
@pytest.mark.asyncio
async def test_hypothesis_add_column(name):
    # 原有测试逻辑不变

3. 临时关闭收缩阶段快速定位

如果开了verbose日志还是被后续报错刷得找不到原始错误,可以临时把重跑、收缩阶段关掉,Hypothesis找到第一个失败用例后不会做重复验证,直接抛当前错误:

from hypothesis import settings, Phase

@given(st.text(min_size=1, max_size=128))
@settings(
    phases=[Phase.generate], # 只保留用例生成阶段,去掉重跑、收缩逻辑
    report_multiple_bugs=False
)
@pytest.mark.asyncio
async def test_hypothesis_add_column(name):
    # 原有测试逻辑不变

注意:这个方法仅适合临时调试用,因为关掉收缩阶段后Hypothesis不会给你返回最小复现用例,调试完记得改回默认配置,再配合fixture清理做稳定测试。


内容的提问来源于stack exchange,提问作者Yann PIQUET

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 14:48:34