跳转到主要内容

支持测试代码

项目描述

nti.testing

Latest release Supported Python versions Test Status https://coveralls.io/repos/github/NextThought/nti.testing/badge.svg Documentation Status

支持编写测试,特别是在Zope3/ZTK环境中使用zope.testing(nose2也可能工作,但不建议使用)。

完整文档托管在 https://ntitesting.readthedocs.io/

安装

nti.testing可以使用pip安装,无论是从git仓库还是从PyPI安装。

pip install nti.testing[testgres]

使用testgres额外功能,可以使用nti.testing.layers.postgres

PyHamcrest

nti.testing提供了一组PyHamcrest匹配器。这些匹配器包括通用匹配器和适用于zope.interfacezope.schema用户的匹配器。

匹配器可以从 nti.testing.matchers 模块中导入;最常用的匹配器可以直接从 nti.testing 中导入。

基本匹配器

is_trueis_false 检查提供的对象(出于解释目的,我们使用字面量,但显然当匹配的对象是一个变量时(通常是一个更复杂类型的变量)更有意义,并且读起来更好)的布尔值。

>>> from hamcrest import assert_that, is_
>>> from nti.testing import is_true, is_false
>>> assert_that("Hi", is_true())
>>> assert_that(0, is_false())

接口匹配器

接下来是支持基本使用 zope.interface 的匹配器。

我们可以检查一个对象是否提供了一个接口,以及一个工厂是否实现了它。

>>> from zope.interface import Interface, Attribute, implementer
>>> class IThing1(Interface):
...     pass
>>> class IThing2(Interface):
...     pass
>>> class IThings(IThing1, IThing2):
...     got_that_thing_i_sent_you = Attribute("Did you get that thing?")
>>> @implementer(IThings)
... class Thing(object):
...     def __repr__(self): return "<object Thing>"
>>> from nti.testing import provides, implements
>>> assert_that(Thing(), provides(IThings))
>>> assert_that(Thing, implements(IThings))

细心的读者会注意到,IThings 定义了一个我们的实现实际上并不提供的属性。这就是下一个更严格的检查出现的地方。 verifiably_provides 使用接口机制来确定接口中指定的所有属性和方法都存在并按描述提供。

>>> from nti.testing import verifiably_provides
>>> assert_that(Thing(), verifiably_provides(IThing2, IThing1))
>>> assert_that(Thing(), verifiably_provides(IThings))
Traceback (most recent call last):
...
AssertionError:...
Expected: object verifiably providing <...IThings>
     but: Using class <class 'Thing'> the object <object Thing> has failed to implement interface ....IThings: The ....IThings.got_that_thing_i_sent_you attribute was not provided.
<BLANKLINE>

如果多个属性或方法未提供,所有此类缺失信息都将被报告。

>>> class IThingReceiver(IThings):
...    def receive(some_thing):
...        """Get the thing"""
>>> @implementer(IThingReceiver)
... class ThingReceiver(object):
...     def __repr__(self): return "<object ThingReceiver>"
>>> assert_that(ThingReceiver(), verifiably_provides(IThingReceiver))
Traceback (most recent call last):
...
AssertionError:...
Expected: object verifiably providing <...IThingReceiver>
     but: Using class <class 'ThingReceiver'> the object <object ThingReceiver> has failed to implement interface ....IThingReceiver:
          The ....IThings.got_that_thing_i_sent_you attribute was not provided
          The ....IThingReceiver.receive(some_thing) attribute was not provided
<BLANKLINE>

zope.interface 只能检查属性或方法是否存在。为了在属性值上放置(任意)更严格的约束,我们可以升级到 zope.schemavalidly_provides 匹配器。

>>> from zope.schema import Bool
>>> class IBoolThings(IThing1, IThing2):
...     got_that_thing_i_sent_you = Bool(required=True)
>>> @implementer(IBoolThings)
... class BoolThing(object):
...     def __repr__(self): return "<object BoolThing>"

validly_providesverifiably_provides 的超集。

>>> from nti.testing import validly_provides
>>> assert_that(BoolThing(), validly_provides(IThing1, IThing2))
>>> assert_that(BoolThing(), validly_provides(IBoolThings))
Traceback (most recent call last):
...
AssertionError:...
Expected: (object verifiably providing <...IBoolThings> and object validly providing ....IBoolThings)
     but: object verifiably providing <....IBoolThings> Using class <class 'BoolThing'> the object <object BoolThing> has failed to implement interface ....IBoolThings: The ....IBoolThings.got_that_thing_i_sent_you attribute was not provided.
<BLANKLINE>

为了更精细的控制,我们可以使用 validated_bynot_validated_by 将数据与模式字段进行比较。

>>> from nti.testing import validated_by, not_validated_by
>>> field = IBoolThings.get('got_that_thing_i_sent_you')
>>> assert_that(True, is_(validated_by(field)))
>>> assert_that(None, is_(not_validated_by(field)))

父子关系

aq_inContextOf 匹配器使用 Acquisition 的概念来检查父子关系。

>>> from nti.testing import aq_inContextOf
>>> class Parent(object):
...     pass
>>> class Child(object):
...     __parent__ = None
>>> parent = Parent()
>>> child = Child()
>>> child.__parent__ = parent
>>> assert_that(child, aq_inContextOf(parent))

测试用例

对测试用例的支持可以在 nti.testing.basenti.testing.layers 中找到。该 base 包包含可直接使用的完整基类,而 layers 包主要包括可以用来构建自己的测试层的混入。

base 包在“正常”和“共享”测试用例之间做出了区分。正常的测试用例是那些用于单个测试用例的测试用例。它们通过 setUp 建立,通过 tearDown 拆除。

相比之下,共享测试用例预计将在类中的所有测试或层中的所有测试期间持续存在。当测试用例创建成本较高时,最好使用这些测试用例。任何从 nti.testing.base.AbstractSharedTestBase 扩展的类都会创建一个共享测试用例。通过元类的魔法,这样的子类也可以被分配给另一个类的 layer 属性,用作可以在多个类之间共享的测试层。

最重要的基类是 nti.testing.base.ConfiguringTestBasenti.testing.base.SharedConfiguringTestBase。这两个都是用于配置 ZCML 的测试用例,无论是从现有包还是完整文件路径。要使用这些,请从它们派生并定义类属性 set_up_packages 和(如果需要)features

>>> from nti.testing.base import ConfiguringTestBase
>>> import zope.security
>>> class MyConfiguringTest(ConfiguringTestBase):
...     set_up_packages = (
...         'zope.component', # the default configuration by name
...          # a named file in a named package
...          ('ftesting.zcml', 'zope.traversing.tests'),
...          # an imported module
...          zope.security,
...          # Our own package; in a test, this will mean the parent
...          # package
...          ".")

然后我们将继续编写我们的测试方法。我们指定的包将在每个测试方法周围建立和拆除。此外,zope.testing 清理函数也将围绕每个测试方法运行。

时间

在每个对 time.time 的调用中都有一个保证以正增方式移动的时钟是有用的。nti.testing.time 提供了一个装饰器来完成这项任务,该装饰器确保值始终至少是当前时间,并且始终是增加的。(它不是线程安全的。)它可以应用于函数或方法,并可选地接受一个 granularity 参数。

>>> from nti.testing import time_monotonically_increases
>>> from nti.testing.time import reset_monotonic_time
>>> @time_monotonically_increases(0.1) # increment by 0.1
... def test():
...     import time
...     t1 = time.time()
...     t2 = time.time()
...     assert t2 == t1 + 0.1, (t2, t1)
>>> test()

以及其他

还有一些其他各种实用工具,包括在 nti.testing.zodb 中支持与 ZODB 一起工作。请参阅 API 文档以获取详细信息。

更改

4.1.0 (2024-04-10)

  • 添加对 testgres 1.10 的支持和要求。这是必需的,因为他们更改了 get_pg_version 的签名。

  • 停止支持Python 3.8和3.9。

4.0.0 (2023-10-24)

  • 支持Python 3.10、3.11和3.12。

  • 停止支持Python 2以及Python 3.6和3.7。

  • 添加一层用于处理 Postgres实例的功能。

  • 向测试基类添加方法以支持unittest.mock补丁。

3.1.0 (2021-09-08)

  • 支持Python 3.9。

  • 停止支持Python 3.5。

  • 添加模块别名nti.testing.mock,该别名可以是标准库unittest.mock,也可以是回滚mock。这允许在向后兼容性很重要的情况下轻松导入。

  • 从nti.testing命名空间直接访问mock、mock.Mock以及各种其他API属性,如is_true。

3.0.0 (2020-06-16)

  • 支持Python 3.8。

  • 要求zope.interface 5.1。这使得接口匹配器可以产生更丰富的错误信息。

  • 添加nti.testing.zodb,用于处理ZODB的辅助工具。这使得ZODB 5.6或以上版本成为必需依赖项。

2.2.1 (2019-09-10)

  • 如果默认事务管理器已被明确指定,则使事务清理更安全。

    此外,将默认事务管理器重置为隐式。

    请参阅问题17

2.2.0 (2018-08-24)

  • 支持Python 3.7。

  • 使time_monotonically_increases也处理time.gmtime,并添加了用于层中使用的辅助工具。

2.1.0 (2017-10-23)

  • 将Acquisition作为可选依赖项。如果未安装,aq_inContextOf匹配器将始终返回False。

  • 删除对fudge的依赖。相反,我们现在在Python 3中使用unittest.mock,或者在Python 2中使用其回滚mock。请参阅问题11

  • 重构ZCML配置支持以共享更多代码和文档。请参阅问题10

  • 层ConfiguringLayerMixin和基类SharedConfiguringTestBase现在默认在子类定义的包中运行配置,就像ConfiguringTestBase的子类一样。

2.0.1 (2017-10-18)

  • 验证匹配器(validated_by和not_validated_by)现在将抛出除zope.interface.exceptions.Invalid(包括zope.schema异常如WrongType)之外的其他任何异常视为失败(默认行为)。以前,它们接受任何异常作为对象无效的标志,但这可能会隐藏实际验证方法中的错误。您可以通过向匹配器提供invalid参数来放宽或收紧此行为。将invalid=Exception提供为将旧行为恢复。)请参阅问题7

2.0.0 (2017-04-12)

  • 支持Python 3.6。

  • 删除unicode_literals。

  • 对time_monotonically_increases进行大量重构以提供更大的安全性。修复问题5

1.0.0 (2016-07-28)

  • 添加Python 3支持。

  • 初始PyPI发布。

项目详情


下载文件

下载适合您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源代码分发

nti.testing-4.1.0.tar.gz (53.2 kB 查看哈希值)

上传时间 源代码

构建分发

nti.testing-4.1.0-py2.py3-none-any.whl (45.4 kB 查看哈希值)

上传时间 Python 2 Python 3

支持