假设(Python):省略论证
Hypothesis (Python): Omit argument
我有一个这样的函数(它实际上是一个 class,但考虑到 Python 的鸭子类型,这不相关):
def myfunc(a=None, b=None):
<snip>
现在我想写一个假设检验,它总是提供 a
,但有时只提供 b
。
我试过了
from hypothesis import given, strategies as strat
@given(a=strat.booleans())
@given(b=strat.integers(min_value=1) | strat.nothing())
def test_model_properties(self, **kwargs):
myval = myfunc(**kwargs)
<snip>
但似乎当它获得 strat.nothing()
时只是跳过该测试 运行(当我将其用作 b
的唯一策略时,我得到 hypothesis.errors.FailedHealthCheck: It looks like your strategy is filtering out a lot of data.
)。
我怎么只能有时提供假设检验的论点?我是否需要编写两个测试,一个有 b
,一个没有?
怎么样:
def myfunc(a=None, b=None):
if b is None:
b=strat.nothing()
# Or whatever you would like b to be when you don't supply an argument
else:
<snip>
所以你让 b
成为默认值(在本例中,None
)触发 myfunc()
内的 'if' 条件,将其设置为其他值。
你的方法肯定会失败,因为,正如 hypothesis docs 暗示的那样
hypothesis.strategies.nothing()[source]
This strategy never successfully draws a value and will always reject on an attempt to draw.
您不为 b
提供值的尝试将永远失败。
这个怎么样:
from hypothesis.strategies import tuples, integers, booleans, one_of
B = booleans()
I = integers(min_value=0, max_value=10)
one_of(tuples(B), tuples(B, I)).example()
经过一系列试验,它给了我 (True,)
、(False, 9)
、(False, 4)
、(True, 5)
和 (False,)
等输出。
当然,您会使用 *args
而不是 **kwargs
。
让我走上正轨——关键词的选择需要有自己的策略。
使用标准词典
std = {'a': strat.booleans()}
和可选字典
opt = {
'b': strat.integers(),
'c': strat.integers(),
}
然后我可以使用 chained list comprehension 来获得所有可能的 "optional argument combinations":
# chain.from_iterable may or may not be faster; it doesn't matter for me.
optional = [combo
for n in range(len(opt.items()))
for combo in itertools.combinations(opt.items(), n+1)]
为 b
、c
和 (b, c)
生成键值元组。
为了绘制一组值,我们需要获得其中一个选项,这可以通过 sampled_from(optional)
完成。对于得到的元组,除了std
字典中的策略外,我们还必须从中提取策略。
strat.sampled_from(optional).flatmap(
lambda x: strat.fixed_dictionaries(
{**std, **dict(x)}
)
)
这些都可以封装在一个函数里,姑且称之为valid_values()
。如果在包装函数的签名中指定 *args
或 **kwargs
,则不能使用 @given(valid_values())
。
因此,test_model_properties(self, **kwargs)
变为 test_model_properties(self, kwargs)
(您可以使用 @given(kwargs=valid_values())
)- 通过调用字典 kwargs
,其余功能保持不变。
注意:如果您想要没有可选参数的可能性,这将不包括空元组,但可以轻松地将其附加到 optional
列表。或者,使用 range(n+1)
而不是 combinations(..., n+1)
,因此包括长度 0.
看起来你想要 none()
而不是 nothing()
:
from hypothesis import given, strategies as strat
@given(a=strat.booleans(), b=strat.none() | strat.integers(min_value=1))
def test_model_properties(self, **kwargs):
myval = myfunc(**kwargs)
...
这比生成字典以用作 **kwargs 更简单,而且效率也更高一些。 b
的策略顺序也很重要——将 none()
放在首位可确保最小示例为 a=False, b=None
而不是 a=False, b=1
.
另请注意,与单次使用相比,多次应用 @given
效率非常低,实际上自版本 3.34.0 起已弃用。
我有一个这样的函数(它实际上是一个 class,但考虑到 Python 的鸭子类型,这不相关):
def myfunc(a=None, b=None):
<snip>
现在我想写一个假设检验,它总是提供 a
,但有时只提供 b
。
我试过了
from hypothesis import given, strategies as strat
@given(a=strat.booleans())
@given(b=strat.integers(min_value=1) | strat.nothing())
def test_model_properties(self, **kwargs):
myval = myfunc(**kwargs)
<snip>
但似乎当它获得 strat.nothing()
时只是跳过该测试 运行(当我将其用作 b
的唯一策略时,我得到 hypothesis.errors.FailedHealthCheck: It looks like your strategy is filtering out a lot of data.
)。
我怎么只能有时提供假设检验的论点?我是否需要编写两个测试,一个有 b
,一个没有?
怎么样:
def myfunc(a=None, b=None):
if b is None:
b=strat.nothing()
# Or whatever you would like b to be when you don't supply an argument
else:
<snip>
所以你让 b
成为默认值(在本例中,None
)触发 myfunc()
内的 'if' 条件,将其设置为其他值。
你的方法肯定会失败,因为,正如 hypothesis docs 暗示的那样
hypothesis.strategies.nothing()[source]
This strategy never successfully draws a value and will always reject on an attempt to draw.
您不为 b
提供值的尝试将永远失败。
这个怎么样:
from hypothesis.strategies import tuples, integers, booleans, one_of
B = booleans()
I = integers(min_value=0, max_value=10)
one_of(tuples(B), tuples(B, I)).example()
经过一系列试验,它给了我 (True,)
、(False, 9)
、(False, 4)
、(True, 5)
和 (False,)
等输出。
当然,您会使用 *args
而不是 **kwargs
。
使用标准词典
std = {'a': strat.booleans()}
和可选字典
opt = {
'b': strat.integers(),
'c': strat.integers(),
}
然后我可以使用 chained list comprehension 来获得所有可能的 "optional argument combinations":
# chain.from_iterable may or may not be faster; it doesn't matter for me.
optional = [combo
for n in range(len(opt.items()))
for combo in itertools.combinations(opt.items(), n+1)]
为 b
、c
和 (b, c)
生成键值元组。
为了绘制一组值,我们需要获得其中一个选项,这可以通过 sampled_from(optional)
完成。对于得到的元组,除了std
字典中的策略外,我们还必须从中提取策略。
strat.sampled_from(optional).flatmap(
lambda x: strat.fixed_dictionaries(
{**std, **dict(x)}
)
)
这些都可以封装在一个函数里,姑且称之为valid_values()
。如果在包装函数的签名中指定 *args
或 **kwargs
,则不能使用 @given(valid_values())
。
因此,test_model_properties(self, **kwargs)
变为 test_model_properties(self, kwargs)
(您可以使用 @given(kwargs=valid_values())
)- 通过调用字典 kwargs
,其余功能保持不变。
注意:如果您想要没有可选参数的可能性,这将不包括空元组,但可以轻松地将其附加到 optional
列表。或者,使用 range(n+1)
而不是 combinations(..., n+1)
,因此包括长度 0.
看起来你想要 none()
而不是 nothing()
:
from hypothesis import given, strategies as strat
@given(a=strat.booleans(), b=strat.none() | strat.integers(min_value=1))
def test_model_properties(self, **kwargs):
myval = myfunc(**kwargs)
...
这比生成字典以用作 **kwargs 更简单,而且效率也更高一些。 b
的策略顺序也很重要——将 none()
放在首位可确保最小示例为 a=False, b=None
而不是 a=False, b=1
.
另请注意,与单次使用相比,多次应用 @given
效率非常低,实际上自版本 3.34.0 起已弃用。