假设库:补充一些其他策略的策略

Hypothesis library: strategy for the complement of some other strategy

我正在使用 Hypothesis 库进行单元测试。使用此库,您无需手动选择输入,而是定义要测试的完整输入集。 Hypothesis 然后将从这个集合中抽样以寻找破坏函数的输入。这也称为基于 属性 的测试。在假设中,这些集合称为策略。

现在我想对验证某些输入的函数进行单元测试:

GRIDSIZE_FORMAT = "^[0-9]+x[0-9]+$"
CELL_FORMAT = "^[A-Z]+[0-9]+$"

def _validate(gridsize, walls, entrance):
    if not re.match(GRIDSIZE_FORMAT, gridsize):
        raise ValueError(f"grid size '{gridsize}' does not match format '{GRIDSIZE_FORMAT}'")
    for wall in walls:
        if not re.match(CELL_FORMAT, walls):
            raise ValueError(f"wall '{wall}' does not match format '{CELL_FORMAT}'")
    if not re.match(CELL_FORMAT, entrance):
        raise ValueError(f"entrance '{entrance}' does not match format '{CELL_FORMAT}'")

为了正确测试此函数,我想生成“除 X 之外的任何内容”形式的示例,X 是此函数的正确输入格式。

Hypothesis 库中是否有可以生成这样输入的策略?

我确实找到了使用正则表达式执行此操作的方法:

from hypothesis import strategies as st

not_cellpattern = st.from_regex(f'(?!{CELL_FORMAT})')
@given(
    gridsize = st.from_regex(f'(?!{GRIDSIZE_FORMAT})'),
    walls = st.lists(not_cellpattern),
    entrance = not_cellpattern
)
def test_validate(gridsize, walls, entrance):
    try:
        _validate(gridsize, walls, entrance)
    except ValueError:
        pass
    else:
        raise Exception(f"_validate did not catch faulty input '{gridsize}', '{walls}', '{entrance}'")

但这并不完全正确:因为 1) 我必须将自己限制在字符串上,以及 2) 这只测试所有三个输入都错误的情况,而不仅仅是其中一个输入错误。

不幸的是,Hypothesis 无法计算策略的补充,因为策略可以由任意 user-supplied 代码(包括 side-effects!)组成。比如hypothesis.extra.django.from_model(User)的补码应该是什么,将生成的实例插入到数据库中? (无效,不在数据库中,...)

在更具体的情况下,您可以采用您的规范的补充,然后从该补充中推导出策略。您的正则表达式技巧是一个很好的例子 - 显式 set-complement 往往比天真的“生成一些东西 right-ish 并过滤掉有效实例”方法更有效。