为何使用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
相关产品推荐
相关产品推荐

