测试基本Python数据结构接口

时间:2013-05-16 18:13:41

标签: python testing data-structures

一个相当小的问题:有没有人知道预先制作的Python单元测试套件,只检查一个类是否符合标准Python数据结构接口之一(例如,列表,集合,字典,队列等) 。写它们并不是很难,但是如果有人已经这样做的话,我不愿意这样做。这似乎是某人可能已经完成的非常基本的功能。

用例是由于与平台相关的不同限制,我使用工厂模式来创建数据结构。因此,我需要能够测试生成的对象仍然符合表面上的标准接口。此外,我应该注意,通过“符合”,我的意思是测试不仅要检查接口函数是否存在,还要检查它们是否有效(例如,可以设置和检索映射中的值)。 Python 2.7测试将是首选。

2 个答案:

答案 0 :(得分:3)

首先,“标准Python数据结构接口”不是列表,集合,字典,队列等。这些是接口的特定实现。 (并且队列甚至不是您正在考虑的意义上的数据结构 - 其显着特征是其操作是原子的,并且putget可选地在Condition上同步等等。)

无论如何,接口是以五种不同的不兼容方式定义的。


文档的Built-in Types部分描述了迭代器类型,序列类型等的含义。然而,这些并不像您期望的参考文档那么严格(至少如果你“习惯了,比方说,C ++或Java。”

我不知道对这样的事情有任何测试,所以我认为你必须从头开始构建它们。


collections模块包含定义接口的Collections Abstract Base Classes,并提供通过abc module注册“虚拟子类”的方法。因此,您可以通过继承collections.Mapping或调用collections.Mapping.register来声明“我是映射”。但这实际上并不能证明你一个映射,就像你声称的那样。 (如果您继承自Mapping,它还可以充当mixin,通过在__contains__之上实施__getitem__来帮助您完成界面。)

如果你想测试ABC的含义,defuz的答案非常接近,而且我认为他或其他人可以完成它。


CPython C API定义了Abstract Objects Layer。虽然这对于该语言实际上并不具有权威性,但显然C-API协议和语言级接口应该匹配。并且,与后者不同,前者是严格定义的。当然,CPython 2.7的源代码以及PyPy等其他实现可能会有所帮助。

CPython附带了这方面的测试,但实际上,它们用于测试从C调用PyMapping_GetItem正确调用Python中的mymapping.__getitem__,这实际上与你的相切想要测试,所以我认为它不会有多大帮助。


实际的具体类在协议之上有额外的接口,您可能想要测试,但这更难描述。特别是,__new____init__方法的工作方式通常很重要。实现Mapping协议意味着有人可以构建一个空的Foo实例并使用foo[key] = value向其添加项目,但意味着有人可以构建Foo(key=value) 1}},或Foo({key: value})Foo([(key, value)])

对于这种情况,是所有标准Python实现附带的现有测试。 CPython附带了一个非常广泛的测试套件,其中包括test_dict.py之类的内容。 PyPy运行所有(Python级)CPython测试,还有一些额外的测试。

您显然必须修改这些测试以在任意类上运行而不是在测试中硬编码,并且您可能还必须修改它们以处理您选择的任何定义。此外,他们可能会测试更多而不是您的要求。你只想知道一个类是否符合协议,而不是它的方法是否正确,对吧?但是,我认为它们是一个很好的起点。


最后,C API定义了一个Concrete Objects Layer,虽然它不具有权威性,但它与之前的定义相匹配,并且更严格地定义。

不幸的是,这个测试肯定对你不是很有用,因为他们正在检查PyDict_CheckPyDict_GetItem是否适合你class,它们不会用于纯Python中定义的任何映射。


如果您确实为这些定义构建了完整的内容,我强烈建议将其放在PyPI上,并将其发布到python-list,以便获得反馈(以及错误报告)。

答案 1 :(得分:0)

基于ABC模块的标准模块collections中有abstract base classes

您必须从这些类继承您的类,以确保您的类符合标准行为:

import collections

class MyDict(collections.Mapping):
    ...

此外,您可以测试已经存在的类,它显然没有继承抽象类:

class MyPerfectDict(object):
    ... realization ...

def is_inherit(cls, abstract):
    try:
        class Test(abstract, cls): pass
        test = Test()
    except TypeError:
        return False
    else:
        return True

is_inherit(MyPerfectDict, Mapping) # False
is_inherit(dict, Mapping) # True