给定一个我试图构造的对象:
from dataclasses import dataclass
@dataclass
class Thing:
thing_id: str
a: int
b: float
# other attributes
对于基于属性的测试,我需要生成Thing
列表,以便
item_id
在列表中是唯一的a
和b
是列表[(a0, b0), (a1, b1), ...]
中的配对值- 从该
(a, b)
列表中,每个值至少绘制一次
这就是我想出的:
from hypothesis import strategies as st
def thing_lists(ab):
# ab = [(a0, b0), (a1, b1), ...]
vals = (
# list that has at least all values from ab
st.lists(st.sampled_from(ab))
.flatmap(lambda sample: st.permutations(sample + ab))
# add ids
.flatmap(
# get unique ids
lambda abs: st.lists(
st.text(), min_size=len(abs), max_size=len(abs), unique=True
# zip with abs
).map(lambda ids: [(id, *ab) for id, ab in zip(ids, abs)])
)
)
return vals.flatmap(
lambda idabs: st.tuples(
*[
st.builds(
Thing,
st.just(id),
st.just(a),
st.just(b),
# other attributes by free choice of hypothesis
)
for (id, a, b) in idabs
]
).map(list)
)
这有效,但绘制一个元组然后将其映射到列表有点复杂;我是否错过了一种更清楚地了解正在发生的事情的不同技术?
这对我来说基本上是合理的 - 它很邋遢,但主要是因为你的要求。 我也会尝试
vals = st.lists(
st.tuples(st.text(), st.sampled_from(ab)),
min_size=len(ab),
unique_by=lambda x: x[0],
).filter(lambda ls: {x for _, x in ls}.issuperset(ab))
但显然,如果ab
很长,该过滤器将是一个严格的约束,所以我只期望如果它很短(但要衡量!
如果这确实有效,你会得到比上面更快,有时更好的收缩;内联builds()
部分以使每个列表元素的生成完全本地将进一步改善这一点,但可能需要一个新的构造函数。 不过,这些都不值得牺牲可读性!