合并合约和自动测试。
项目描述
icontract-hypothesis
Icontract-hypothesis将设计-by-contract与自动测试相结合。
它是设计-by-contract库icontract和属性测试库Hypothesis之间的集成。
结果是强大的组合,允许您自动测试您的代码。您无需手动编写函数的Hypothesis搜索策略,icontract-hypothesis可以根据函数的前置条件推断它们。这使得自动测试变得非常简单。
您可以使用icontract-hypothesis
作为一个库,编写简洁的单元测试,
作为一个命令行工具或作为集成在您的IDE(例如,icontract-hypothesis-vim,icontract-hypothesis-pycharm和icontract-hypothesis-vscode)中的工具。
这允许您在开发过程中自动测试函数,并在持续集成中使用它,
作为一个幕后工具,为您提供更详细的Hypothesis策略的起点。
由于合约靠近代码,因此代码的演变也会自动演变测试。
用法
库
有两种方法可以将icontract-hypothesis作为库集成到您的测试中。
仅假设。 首先,您可以使用它来根据前置条件定义测试的假设
>>> from hypothesis import given
>>> import hypothesis.strategies as st
>>> import icontract
>>> import icontract_hypothesis
>>> @icontract.require(lambda x: x > 0)
... @icontract.ensure(lambda result: result > 0)
... def some_func(x: int) -> int:
... return x - 1000
>>> assume_preconditions = icontract_hypothesis.make_assume_preconditions(
... some_func)
>>> @given(x=st.integers())
... def test_some_func(x: int) -> None:
... assume_preconditions(x)
... some_func(x)
>>> test_some_func()
Traceback (most recent call last):
...
icontract.errors.ViolationError: File <doctest README.rst[4]>, line 2 in <module>:
result > 0:
result was -999
x was 1
由 icontract_hypothesis.make_assume_preconditions 创建的函数 assume_preconditions 将拒绝所有不满足 some_func 预条件的输入值。
推断策略。其次,您可以自动推断策略并测试函数。
>>> import icontract
>>> import icontract_hypothesis
>>> @icontract.require(lambda x: x > 0)
... @icontract.ensure(lambda result: result > 0)
... def some_func(x: int) -> int:
... return x - 1000
>>> icontract_hypothesis.test_with_inferred_strategy(some_func)
Traceback (most recent call last):
...
icontract.errors.ViolationError: File <doctest README.rst[10]>, line 2 in <module>:
result > 0:
result was -999
x was 1
选择哪种方法取决于您如何编写测试。第一种方法,使用 assume_preconditions,如果您已经定义了搜索策略并且只想排除几个边缘情况,则非常实用。第二种方法,自动推断测试策略,如果您只想测试函数而不指定任何特定的搜索策略,则非常有用。
Icontract-hypothesis 保证推断的策略必须满足预条件。如果不满足,请考虑这是一个错误,在这种情况下,请创建一个问题,以便我们可以修复它。
如果您想检查策略或进一步程序化地改进它,请使用 icontract_hypothesis.infer_strategy
>>> import math
>>> import icontract
>>> import icontract_hypothesis
>>> @icontract.require(lambda x: x > 0)
... @icontract.require(lambda x: x > math.sqrt(x))
... def some_func(x: float) -> int:
... pass
>>> icontract_hypothesis.infer_strategy(some_func)
fixed_dictionaries({'x': floats(min_value=0, exclude_min=True).filter(lambda x: x > math.sqrt(x))})
测试工具
我们提供了一个命令行工具 pyicontract-hypothesis test,您可以使用它来自动测试一个模块。
usage: pyicontract-hypothesis test [-h] -p PATH
[--settings [SETTINGS [SETTINGS ...]]]
[--inspect] [-i [INCLUDE [INCLUDE ...]]]
[-e [EXCLUDE [EXCLUDE ...]]]
optional arguments:
-h, --help show this help message and exit
-p PATH, --path PATH Path to the Python file to test
--settings [SETTINGS [SETTINGS ...]]
Specify settings for Hypothesis
The settings are assigned by '='.
The value of the setting needs to be encoded as JSON.
Example: max_examples=500
--inspect Only show the strategy and the settings
No tests are executed.
-i [INCLUDE [INCLUDE ...]], --include [INCLUDE [INCLUDE ...]]
Regular expressions, lines or line ranges of the functions to process
If a line or line range overlaps the body of a function,
the function is considered included.
Example 1: ^do_something.*$
Example 2: 3
Example 3: 34-65
-e [EXCLUDE [EXCLUDE ...]], --exclude [EXCLUDE [EXCLUDE ...]]
Regular expressions, lines or line ranges of the functions to exclude
If a line or line range overlaps the body of a function,
the function is considered excluded.
Example 1: ^do_something.*$
Example 2: 3
Example 3: 34-65
请注意,如果可以传递当前光标位置和当前文件名,则 pyicontract-hypothesis test 可以轻松集成到您的 IDE 中。
自动写作工具
手动编写基于属性的测试是繁琐的,可以部分自动化。为此,我们实现了自动写作实用程序 pyicontract-hypothesis ghostwrite,它基于您手动细化的预条件生成一个初稿。
usage: pyicontract-hypothesis ghostwrite [-h] (-m MODULE | -p PATH)
[-o OUTPUT] [--explicit] [--bare]
[-i [INCLUDE [INCLUDE ...]]]
[-e [EXCLUDE [EXCLUDE ...]]]
optional arguments:
-h, --help show this help message and exit
-m MODULE, --module MODULE
Module to ghostwrite the unit tests for
-p PATH, --path PATH Path to the module to ghostwrite the unit tests for.
If the file represents a module reachable through
sys.path, use the qualified module name in
the unit test.
Otherwise, the module is represented as the stem
of the path with all non-identifier characters
replaced with an underscore ("_").
-o OUTPUT, --output OUTPUT
Path to the file where the output should be written.
If '-', writes to STDOUT.
--explicit Write the inferred strategies explicitly
This is practical if you want to tune and
refine the strategies and just want to use
ghostwriting as a starting point.
Mind that pyicontract-hypothesis does not
automatically fix imports as this is
usually project-specific. You have to fix imports
manually after the ghostwriting.
--bare Print only the body of the tests and omit header/footer
(such as TestCase class or import statements).
This is useful when you only want to inspect a single test or
include a single test function in a custom test suite.
-i [INCLUDE [INCLUDE ...]], --include [INCLUDE [INCLUDE ...]]
Regular expressions, lines or line ranges of the functions to process
If a line or line range overlaps the body of a function,
the function is considered included.
Example 1: ^do_something.*$
Example 2: 3
Example 3: 34-65
-e [EXCLUDE [EXCLUDE ...]], --exclude [EXCLUDE [EXCLUDE ...]]
Regular expressions, lines or line ranges of the functions to exclude
If a line or line range overlaps the body of a function,
the function is considered excluded.
Example 1: ^do_something.*$
Example 2: 3
Example 3: 34-65
自动写作测试的示例可在:tests/pyicontract_hypothesis/samples 中找到
作为模块运行工具
如果您出于某种原因想以模块形式运行工具,只需调用即可。
usage: icontract_hypothesis [-h] {test,ghostwrite} ...
Combine property-based testing with contracts of a Python module.
positional arguments:
{test,ghostwrite} Commands
test Test the functions automatically by inferring search
strategies from preconditions
ghostwrite Ghostwrite the unit tests with inferred search strategies
optional arguments:
-h, --help show this help message and exit
安装
icontract-hypothesis 可在 PyPI 上找到,网址为 https://pypi.ac.cn/project/icontract-hypothesis,因此您可以使用 pip
pip3 install icontract-hypothesis
搜索策略
模糊测试的简单方法是从随机样本输入数据,根据预条件过滤它,并在运行后确保后置条件。但是,如果您的可接受输入值范围很窄,拒绝抽样会变得不切实际地慢。
例如,假设有一个预条件 5 < x < 10。对所有可能的整数 x 进行采样很少会命中预条件(如果有的话),从而浪费宝贵的计算时间。随着参数数量的增加,由于 维度诅咒,问题会加剧。
Icontract-hypothesis 尝试更智能地解决搜索策略
预条件与常见的代码模式相匹配以定义策略。例如,5 < x < 10 给出搜索策略 hypothesis.strategies.integers(min=6, max=9)。
我们目前匹配所有可用的 Hypothesis 类型(int、float、datetime.date 等)的界限。我们还在 str 参数上匹配正则表达式。
无法匹配的预条件,但操作单个参数的预条件基于类型提示推断,并与 Hypothesis FilteredStrategy 组合。
剩余的预条件通过在最终传递给函数的整个固定字典上过滤来强制执行。
有一个正在进行中的努力,将策略匹配代码移动到 Hypothesis 并进一步开发它以包括更多情况。请参阅 此 Hypothesis 问题。
请注意,源代码的静态分析可能无法确定各种作用域中定义的所有名称,因为它们也可以动态注入(例如,设置 __globals__ 属性或 globals()[random.choice("abc")] = 1)。只要您不将复杂的动态技巧用于您的合约,icontract-hypothesis 的策略推理应该能正常工作。
类
Hypothesis 会自动构建复合输入参数(类、数据类、命名元组等)。如果您的类在其构造函数(__init__)中强制执行前置条件,请确保它继承自 icontract.DBC。
这样,icontract-hypothesis 将使用 hypothesis.strategies.register_type_strategy 将您的类注册到 Hypothesis,并在构建其实例时考虑前置条件。
重要的是您不应该使用 hypothesis.strategies.builds 与在构造函数中使用合约的类,因为 builds 将忽略已注册的策略。您应该使用 hypothesis.strategies.from_type 代替。参见 这个 Hypothesis 问题的评论 和 相应的答案。
通常,构造函数的默认推断策略应该足够,尽管您当然不限于这些。您可以使用 hypothesis.strategies.register_type_strategy 注册自己的策略。Icontract-hypothesis 会尊重以前的注册,而不会覆盖它们。
IDE 插件
版本控制
我们遵循语义版本控制。版本X.Y.Z表示
X是主版本(不向后兼容),
Y是次版本(向后兼容),
Z是补丁版本(向后兼容的bug修复)。
项目详细信息
icontract-hypothesis-1.1.7.tar.gz的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 40f5be35ae5a7d32b7058b579b1eb574367c80709de8bebb52bef685aa01662f |
|
MD5 | a7afaa66d53ba9047225edd3462ec608 |
|
BLAKE2b-256 | 8d8e958e577be5a3f74e87a83cb85cad3e5cd16d856079eee1daa015c26a751c |