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

为何使用st.recursive()而非st.deferred()?二者行为有何差异?

关于Hypothesis中两种JSON测试策略的行为差异问题

我需要生成任意JSON值的测试策略,在阅读《使用composite()处理递归数据的陷阱》一文后,编写了基于st.recursive的实现:

json_primitives = st.one_of(
    st.none(),
    st.booleans(),
    st.integers(),
    st.floats(allow_infinity=False, allow_nan=False),
    st.text(),
)

def json_collections(values):
    return st.one_of(
        st.dictionaries(keys=st.text(), values=values),
        st.lists(values),
    )

json_values = st.recursive(json_primitives, json_collections)

同时在Hypothesis自身测试中发现了基于st.deferred的实现:

json_values = st.deferred(
    lambda: st.none()
    | st.booleans()
    | st.integers()
    | st.floats(allow_infinity=False, allow_nan=False)
    | st.text()
    | json_arrays
    | json_objects
)
json_arrays = st.lists(json_values)
json_objects = st.dictionaries(st.text(), json_values)

请问这两种策略的行为是否存在差异?我查看了二者的实现,发现st.deferred的实现更易理解,且使用起来也更易读。


两种策略的行为差异

1. 递归深度控制机制不同

  • st.recursive是Hypothesis专为递归数据设计的工具,会自动控制递归深度:默认从浅到深生成数据,逐步提升结构复杂度,内部有成熟的防无限递归/栈溢出机制。它的逻辑是先基于基础类型(json_primitives)生成数据,再通过传入的json_collections函数逐步嵌套生成更深层级的结构。
  • st.deferred仅解决了定义时的循环引用问题,不会主动控制递归深度。如果无额外限制,可能生成极深的嵌套结构,甚至在极端场景下触发栈溢出(Hypothesis生成器有基础防护,但针对性远不如st.recursive)。

2. 数据生成分布有区别

  • st.recursive会偏向于先生成更多浅层次数据,再逐步覆盖复杂嵌套结构,这种分布更符合测试逻辑——先验证简单场景,再深入复杂场景。
  • st.deferred生成的数据深度分布相对随机,没有内置的“从浅到深”优先级,若需要控制分布需手动干预。

3. 使用灵活性与针对性不同

  • st.recursive的针对性更强,适合快速构建可控的递归数据策略,无需手动拆分集合类型(数组、对象),只需定义基础类型和集合生成逻辑即可。
  • st.deferred的语法更直观自由,能清晰拆分出json_arrays、json_objects等独立子策略,便于单独复用或调整,但如果需要控制递归深度,需额外通过recursion_limit参数或自定义逻辑实现。

内容的提问来源于stack exchange,提问作者Chris Wesseling

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 12:20:34