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

使用st.deferred()与Hypothesis生成指定键值策略字典未达预期求助

问题:Hypothesis生成字典时键值策略映射错误

需要生成类复合类结构的测试字典,要求name键对应字符串值,fanout_ref键对应布尔值列表。使用Hypothesis的st.deferred结合lambda实现时,出现键值策略不匹配的问题:值可能是键名本身,或者错误的策略类型(比如name对应布尔列表)。

错误代码

name_strategy = st.text(min_size=0, max_size=10)
fanout_ref_strategy = st.lists(st.booleans())

MyClass_keys = [
    "name",
    "fanout_ref",
]  

MyClass_strategy_mapping = {
    "name": name_strategy,
    "fanout_ref": fanout_ref_strategy,
}
MyClass_value_strategy = st.deferred(lambda: MyClass_key_strategy.map(MyClass_strategy_mapping.get) | MyClass_key_strategy)
MyClass_key_strategy = st.sampled_from(MyClass_keys)
MyClass_strategy = st.dictionaries(keys=MyClass_key_strategy, values=MyClass_value_strategy)

@given(specific_dict=MyClass_strategy)
def test_specific_dictionary(specific_dict):
    # Your test code here
    print(specific_dict)

错误输出

{}
{}
{'name': text(max_size=10)}
{'fanout_ref': 'name', 'name': text(max_size=10)}
{'fanout_ref': text(max_size=10), 'name': text(max_size=10)}
{'fanout_ref': lists(booleans())}
{'fanout_ref': lists(booleans()), 'name': lists(booleans())}
{'fanout_ref': text(max_size=10), 'name': lists(booleans())}
{'name': lists(booleans()), 'fanout_ref': lists(booleans())}
{'name': lists(booleans()), 'fanout_ref': 'fanout_ref'}
{'name': text(max_size=10)}
{'name': text(max_size=10)}

问题原因

  1. MyClass_value_strategy中的map(MyClass_strategy_mapping.get)返回的是策略对象(比如text(max_size=10)),而不是策略生成的实际值,这是对map方法的误用。
  2. | MyClass_key_strategy分支会直接将键名作为值生成,完全不符合需求。
  3. st.dictionaries的values参数如果是固定策略,无法根据不同的键动态选择对应的生成逻辑。

解决方法

使用Hypothesis的value_factory参数(Hypothesis 6.0及以上版本支持),可以为每个生成的键指定对应的取值策略,这是最简洁的解决方案:

import hypothesis.strategies as st

name_strategy = st.text(min_size=0, max_size=10)
fanout_ref_strategy = st.lists(st.booleans())

MyClass_strategy_mapping = {
    "name": name_strategy,
    "fanout_ref": fanout_ref_strategy,
}

MyClass_strategy = st.dictionaries(
    keys=st.sampled_from(list(MyClass_strategy_mapping.keys())),
    # 为每个键匹配对应的生成策略
    value_factory=lambda key: MyClass_strategy_mapping[key],
    # 可选:确保字典至少包含一个键,避免空字典
    min_size=1
)

@given(specific_dict=MyClass_strategy)
def test_specific_dictionary(specific_dict):
    print(specific_dict)
    # 验证值类型符合预期
    for key, value in specific_dict.items():
        if key == "name":
            assert isinstance(value, str)
        elif key == "fanout_ref":
            assert isinstance(value, list)
            assert all(isinstance(b, bool) for b in value)

如果需要更灵活的生成逻辑(比如动态决定键的数量或嵌套结构),可以使用st.data()手动控制生成流程:

import hypothesis.strategies as st
from hypothesis import given, data

name_strategy = st.text(min_size=0, max_size=10)
fanout_ref_strategy = st.lists(st.booleans())

MyClass_keys = ["name", "fanout_ref"]
MyClass_strategy_mapping = {
    "name": name_strategy,
    "fanout_ref": fanout_ref_strategy,
}

@given(d=data())
def test_specific_dictionary(d):
    # 随机选择要包含的键(至少一个)
    selected_keys = d.draw(st.sets(st.sampled_from(MyClass_keys), min_size=1))
    specific_dict = {}
    for key in selected_keys:
        # 根据键选择对应的策略生成值
        specific_dict[key] = d.draw(MyClass_strategy_mapping[key])
    print(specific_dict)
    # 验证值类型
    for key, value in specific_dict.items():
        if key == "name":
            assert isinstance(value, str)
        elif key == "fanout_ref":
            assert isinstance(value, list)
            assert all(isinstance(b, bool) for b in value)

效果说明

修改后生成的字典会严格匹配键值策略:

  • name键的值始终是长度0-10的字符串
  • fanout_ref键的值始终是布尔值组成的列表
  • 不会再出现空字典、键名作为值、策略对象作为值的错误情况

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 23:50:38