丰富的匹配器,适用于测试中的断言。受Hamcrest启发。
项目描述
Precisely 允许您编写精确的断言,以便您只需测试您真正感兴趣的行为。这使得读者更清楚地了解预期的行为,并使测试更不易破碎。这还允许在断言失败时生成更好的错误消息。受Hamcrest启发。
例如,假设我们想确保一个 唯一 函数从一个列表中删除重复项。我们可能会这样编写测试
from precisely import assert_that, contains_exactly
def test_unique_removes_duplicates():
result = unique(["a", "a", "b", "a", "b"])
assert_that(result, contains_exactly("a", "b"))
只要 result 包含 "a" 和 "b"(顺序不限),断言就会通过,但不会有其他项目。与 assert result == ["a", "b"] 不同,我们的断言忽略了元素顺序。这在以下情况下很有用:
结果的顺序是非确定性的,例如使用 set。
在 唯一 的合同中未指定顺序。如果我们断言特定的顺序,那么我们就会测试实现而不是合同。
在 唯一 的合同中指定了顺序,但顺序在单独的测试用例中进行了测试。
当断言失败时,错误信息不仅会说明两个值不相等,还会更详细地描述失败原因。例如,如果unique的值为["a", "a", "b"],我们会得到以下错误信息:
Expected: iterable containing in any order: * 'a' * 'b' but: had extra elements: * 'a'
安装
pip install precisely
API
使用assert_that(value, matcher)来断言一个值是否满足匹配器。
许多匹配器是由其他匹配器组成的。如果它们接收到一个匹配器而不是一个值,那么这个值会被equal_to()包装。例如,has_attrs(name="bob")等价于has_attrs(name=equal_to("bob"))。
equal_to(value):如果值等于value,则匹配该值,使用==进行比较。
has_attrs(**kwargs):如果值具有指定的属性,则匹配该值。例如
assert_that(result, has_attrs(id=is_instance(int), name="bob"))
has_attr(attribute_name, matcher):如果值具有指定的属性,则匹配该值。通常情况下,当属性名称是常量时,使用has_attrs被认为是更符合语法的。例如,而不是
assert_that(result, has_attr("id", is_instance(int)))
使用
assert_that(result, has_attrs(id=is_instance(int)))
contains_exactly(*args):如果可迭代对象包含相同的元素(顺序无关),则匹配该可迭代对象。例如
assert_that(result, contains_exactly("a", "b")) # Matches ["a", "b"] and ["b", "a"], # but not ["a", "a", "b"] nor ["a"] nor ["a", "b", "c"]
is_sequence(*args):如果可迭代对象包含相同的元素(顺序相同),则匹配该可迭代对象。例如
assert_that(result, is_sequence("a", "b")) # Matches ["a", "b"] # but not ["b", "a"] nor ["a", "b", "c"] nor ["c", "a", "b"]
includes(*args):如果可迭代对象包含所有指定的元素,则匹配该可迭代对象。例如
assert_that(result, includes("a", "b")) # Matches ["a", "b"], ["b", "a"] and ["a", "c", "b"] # but not ["a", "c"] nor ["a"] assert_that(result, includes("a", "a")) # Matches ["a", "a"] and ["a", "a", "a"] # but not ["a"]
all_elements(matcher):如果可迭代对象中的每个元素都匹配matcher,则匹配该可迭代对象。例如
assert_that(result, all_elements(equal_to(42))) # Matches [42], [42, 42, 42] and [] # but not [42, 43]
is_mapping(matchers):如果映射(如dict)具有相同的键和匹配的值,则匹配该映射。如果映射缺少任何键或有多余的键,将引发错误。例如
assert_that(result, is_mapping({ "a": equal_to(1), "b": equal_to(4), }))
mapping_includes(matchers):如果映射(如dict)具有相同的键和匹配的值,则匹配该映射。如果映射缺少任何键,将引发错误,但允许有额外的键。例如
assert_that(result, mapping_includes({ "a": equal_to(1), "b": equal_to(4), })) # Matches {"a": 1, "b": 4} and {"a": 1, "b": 4, "c": 5} # but not {"a": 1} nor {"a": 1, "b": 5}
anything:匹配所有值。
is_instance(type):如果isinstance(value, type),则匹配任何值。
all_of(*matchers):如果所有子匹配器都匹配,则匹配该值。例如
assert_that(result, all_of( is_instance(User), has_attrs(name="bob"), ))
any_of(*matchers):如果任何子匹配器匹配,则匹配该值。例如
assert_that(result, any_of( equal_to("x=1, y=2"), equal_to("y=2, x=1"), ))
not_(matcher):否定匹配器。例如
assert_that(result, not_(equal_to("hello")))
starts_with(prefix):如果字符串以prefix开头,则匹配该字符串。
contains_string(substring):如果字符串包含substring,则匹配该字符串。
greater_than(value):匹配大于value的值。
greater_than_or_equal_to(value):匹配大于或等于value的值。
less_than(value):匹配小于value的值。
less_than_or_equal_to(value):匹配小于或等于value的值。
close_to(value, delta):如果值在正负delta的容忍度范围内与value相近,则匹配该值。
has_feature(name, extract, matcher):如果extract(value)匹配matcher,则匹配value。例如
assert_that(result, has_feature("len", len, equal_to(2)))
为了清晰起见,通常将has_feature的使用提取到自己的函数中往往有帮助。
def has_len(matcher): return has_feature("len", len, matcher) assert_that(result, has_len(equal_to(2)))
raises(matcher):如果value()抛出与matcher匹配的异常,则匹配value。例如
assert_that(lambda: func("arg"), raises(is_instance(ValueError)))
替代方案
PyHamcrest是另一个Python匹配器的实现。我更喜欢该项目生成的错误消息,但你可以自由地自己判断
# Precisely
from precisely import assert_that, is_sequence, has_attrs
assert_that(
[
User("bob", "jim@example.com"),
User("jim", "bob@example.com"),
],
is_sequence(
has_attrs(username="bob", email_address="bob@example.com"),
has_attrs(username="jim", email_address="jim@example.com"),
)
)
# Expected: iterable containing in order:
# 0: attributes:
# * username: 'bob'
# * email_address: 'bob@example.com'
# 1: attributes:
# * username: 'jim'
# * email_address: 'jim@example.com'
# but: element at index 0 mismatched:
# * attribute email_address: was 'jim@example.com'
# Hamcrest
from hamcrest import assert_that, contains, has_properties
assert_that(
[
User("bob", "jim@example.com"),
User("jim", "bob@example.com"),
],
contains(
has_properties(username="bob", email_address="bob@example.com"),
has_properties(username="jim", email_address="jim@example.com"),
)
)
# Hamcrest error:
# Expected: a sequence containing [(an object with a property 'username' matching 'bob' and an object with a property 'email_address' matching 'bob@example.com'), (an object with a property 'username' matching 'jim' and an object with a property 'email_address' matching 'jim@example.com')]
# but: item 0: an object with a property 'email_address' matching 'bob@example.com' property 'email_address' was 'jim@example.com'
项目详情
下载文件
下载适合您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。