【问题标题】:Pytest: assert a list contains n instances of a class / objectPytest:断言一个列表包含一个类/对象的n个实例
【发布时间】:2022-01-03 23:34:30
【问题描述】:
@dataclass
class Component:
name: str
class Assembly:
def __init__():
names = ["Able", "Baker", "Charlie"]
self.components = [Component(name) for name in names]
def test_assembly():
"""Assembly should be initialised with three Components"""
assembly = Assembly()
assert assembly.components == [Component] * 3 # doesn't work
断言不起作用,因为它将类实例列表 (assembly.components) 与类类型列表进行比较。有没有写这个测试的好方法?
【问题讨论】:
标签:
python
python-3.x
pytest
【解决方案1】:
您可以遍历列表并使用isinstance():
def count_instance(lst, cls):
count = 0
for i in lst:
if isinstance(i, cls):
count += 1
return count
assert count_instance(assembly.components, Component) == 3
【解决方案2】:
您可以使用 map() 将 assembly.components 中的所有元素转换为类型,然后可以将映射转换为列表并使用 list.count 函数查看该类型在列表中出现的次数。像这样的:
assert list(map(type,assembly.components)).count(Component) == 3
如果 assembly.components 中的每个项目都已经是一个类型,您可以确保它是一个列表,然后像这样立即使用 list.count 函数:
assert list(assembly.components).count(Component) == 3
编辑:
此外,如果您发现自己遍历任何内容并想要比较模式出现的次数,我相信使用 int 变量并为您找到的每个模式增加它要好得多,而不是您正在做的事情是创建全新的列表并比较它们大量会很快减速。 (增量方法是我理解 .count 函数的工作方式,所以尽可能尝试使用它。)
【解决方案3】:
assert [type(comp) for comp in assembly.components] == [Component] * 3
这可以完成工作 - 但我怀疑可能有更好的解决方案?
【解决方案4】:
您可以分别断言列表中每个元素的类类型,但是这是一个相对昂贵的操作,假设您已经知道components 中的每个元素都是Component 类型。
相反,您可以只检查列表的长度,如下所示;这假设您不尝试将整数值添加到类构造函数之外的 components 中。尽管您也可以相当轻松地断言列表中的每个元素都是 Component 类型,但使用内置的 map 运算符,然后将迭代器结果解压缩到列表中,这也将显示出来。
注意:注释为 ClassVar 的属性被假定为仅是类或静态属性,因此在构建数据类字段列表时,dataclass 装饰器将忽略此属性。
from dataclasses import dataclass
from typing import ClassVar, List
@dataclass
class Component:
name: str
@dataclass
class Assembly:
names: ClassVar[List[str]] = ["Able", "Baker", "Charlie"]
components: List[Component]
def __init__(self):
self.components = [Component(name) for name in self.names]
def main():
"""Assembly should be initialised with three Components"""
assembly = Assembly()
assert len(assembly.components) == 3 # works
# bonus: check each element is a Component object also
assert assembly.components == [*map(Component, assembly.names)]
if __name__ == '__main__':
main()