Python Hypothesis库中text()策略为何导致自定义策略重试?
Hypothesis composite策略调用次数异常问题
我用composite构建了一个内部调用text()策略的自定义策略,调试FailedHealthCheck.data_too_large错误时发现,调用text()会让自定义策略的执行次数比预期多一倍左右。
我复现了最小示例代码:
import hypothesis from hypothesis import strategies as st def trace(msg): # 用于追踪调用次数的辅助函数 if not hasattr(trace, "counts"): trace.counts = {} trace.counts[msg] = trace.counts.get(msg, 0) + 1 print(f"{msg}: {trace.counts[msg]}") @st.composite def my_custom_strategy(draw, n): """生成包含N个字符串的列表的策略""" trace("a") value = [draw(st.text(max_size=256)) for _ in range(n)] trace("b") return value @hypothesis.given(my_custom_strategy(100)) def test_my_custom_strategy(value): assert len(value) == 100 assert all(isinstance(v, str) for v in value) if __name__ == "__main__": test_my_custom_strategy()
运行后发现trace("a")被调用206次,trace("b")仅被调用100次,多次运行数据一致。更严重的是,调用text()的次数越多,差距呈超线性增长:n=200时,trace("a")被调用305次;n=400时为984次;n≥500时,测试会暂停,仅完成11次迭代而非预期的100次。
原因解释
这是Hypothesis的收缩(shrinking)机制和**健康检查(health check)**共同作用的结果:
- 收缩机制:当Hypothesis生成的测试用例触发失败(这里是
data_too_large健康检查失败),它会尝试收缩用例,找到最小的触发失败的输入。收缩过程中会多次重新执行自定义策略,尝试减少字符串长度、调整生成内容,这就导致trace("a")被多次调用,但只有当策略成功生成符合要求的用例时才会走到trace("b")。 - data_too_large健康检查:当生成的测试数据总大小超过阈值(默认1MB),Hypothesis会触发该检查失败。当n增大时,n个最大256字符的字符串总大小容易接近或超过阈值,Hypothesis会反复尝试生成更小的用例,导致策略调用次数暴增,甚至因多次尝试失败提前终止测试。
- 超线性增长的原因:每次
text()生成的字符串长度随机,n越大,总大小超阈值的概率非线性上升。Hypothesis在收缩和重试时,需要多次重新执行整个自定义策略来调整每个字符串的长度,导致调用次数增长速度远超n的增长速度。
解决建议
- 降低
text()的max_size,减少单条数据大小,避免触发data_too_large检查。 - 若确认数据大小合理,可通过
@hypothesis.settings(suppress_health_check=[hypothesis.HealthCheck.data_too_large])关闭该健康检查(不推荐随意关闭)。 - 改用
st.lists(st.text(max_size=256), min_size=n, max_size=n)代替手动循环draw,Hypothesis对内置lists策略有优化,能减少不必要的重试和收缩次数。
内容的提问来源于stack exchange,提问作者levand
相关产品推荐
相关产品推荐

