跳转到主要内容

提供一些测试助手和一个高级MockTestCase。

项目描述

ftw.testing

此软件包提供编写测试的助手。

集成测试

FTW集成测试层

FTWIntegrationTesting是Plone默认集成测试层的扩展。

主要目标是能够运行FTWIntegrationTesting测试层与ftw.testbrowser的遍历驱动。

数据库隔离和事务

Plone默认集成测试层支持事务:当测试中提交更改时,不提供隔离,提交的更改将出现在下一层。

  • 我们通过在测试设置中创建保存点并在测试清理中回滚到保存点来在测试之间进行隔离。

  • 使用事务拦截器,我们确保测试中的任何代码都不能提交或中止事务。通过使用保存点来模拟事务行为。

使用示例

from ftw.testing import FTWIntegrationTesting
from plone.app.testing import PLONE_FIXTURE
from plone.app.testing import PloneSandboxLayer

class TestingLayer(PloneSandboxLayer):
    defaultBases = (PLONE_FIXTURE,)


TESTING_FIXTURE = TestingLayer()
INTEGRATION_TESTING = FTWIntegrationTesting(
    bases=(TESTING_FIXTURE,),
    name='my.package:integration')

FTW集成测试用例

集成测试用例是一个测试用例基类,提供合理的默认值和实用的助手,用于使用FTWIntegrationTesting测试层测试Plone插件。

您可以在您的包中创建自己的基类,设置默认测试层,并根据您的需要扩展行为和助手。

使用示例

# my/package/tests/test_case.py
from ftw.testing import FTWIntegrationTestCase
from my.package.testing import INTEGRATION_TESTING

class IntegrationTestCase(FTWIntegrationTestCase):
    layer = INTEGRATION_TESTING

Mock测试用例

ftw.testing 提供了一个高级的 MockTestCase,支持从模拟对象注册 Zope 组件(实用程序、适配器、订阅适配器和事件处理器),并在测试拆除期间拆除全局组件注册表。一些功能以前由 plone.mocktestcase 提供,但现在已经不再维护。因此,它们已被复制到本包中。

from ftw.testing import MockTestCase

以下方法可用:

self.create_dummy(**kw)

返回一个非模拟对象,它只是一个带有您传递的任何属性或方法的关键字参数的简单对象。要创建一个模拟方法,请传递一个函数对象或 lambda,例如:self.create_dummy(id="foo", absolute_url=lambda:’http://example.org/foo’)

self.mock_utility(mock, provides, name=u"")`

将给定的模拟对象注册为提供给定接口的全局实用程序,带有给定的名称(默认为无名的默认实用程序)。

self.mock_adapter(mock, provides, adapts, name=u"")`

将给定的模拟对象注册为提供给定接口和适配给定接口的全局适配器,带有给定的名称(默认为无名的默认适配器)。

self.mock_subscription_adapter(mock, provides, adapts)

将给定的模拟对象注册为提供给定接口和适配给定接口的全局订阅适配器。

self.mock_handler(mock, adapts)

将给定的模拟对象注册为给定事件类型的全局事件订阅者。

self.mock_tool(mock, name)

创建一个 getToolByName() 模拟(使用“替换”模式)并配置它,以便调用 getToolByName(context, name) 的代码获得给定的模拟对象。可用于多次:在任一测试夹具中首次调用此方法时,会惰性创建 getToolByName() 模拟。

self.providing_mock(interfaces, *args, **kwargs)

创建一个提供 interfaces 的模拟。

self.mock_interface(interface, provides=None, *args, **kwargs)

创建一个实现 interface 的模拟对象。该模拟不仅提供 interface,还将其用作规范,并断言模拟的方法确实存在于接口上。

self.stub(*args, **kwargs)

创建一个存根。它像模拟一样,但没有断言。

self.providing_stub(interfaces, *args, **kwargs)

创建一个提供 interfaces 的存根。

self.stub_interface(interface, provides=None, *args, **kwargs)

mock_interface 做相同的事情,但禁用预期的调用计数和属性访问计数。请参阅“模拟与存根”以下内容。

self.set_parent(context, parent_context)

模拟 context,使其获取父项为 parent_context。期望至少 context 是一个模拟或存根。返回 context

self.stub_request(interfaces=[], stub_response=True, content_type='text/html', status=200)

返回一个可以用于渲染模板的请求存根。使用 stub_response 选项,您可以定义请求是否应该自己模拟响应。其他可选参数: content_type:定义响应的预期输出内容类型。 status:定义响应的预期状态码。

self.stub_response(request=None, content_type='text/html', status=200))

返回一个带有一些头和选项的存根响应。当提供 request 时,该响应也添加到给定的请求中。其他可选参数: content_type:定义响应的预期输出内容类型。 status:定义响应的预期状态码。

组件注册层

MockTestCase 能够模拟组件(适配器、实用程序)。它在每次测试后清理组件注册表。

但是当我们使用ZCML层时,为同一层的所有测试加载该包的ZCML,应该使用相同的组件注册表。ComponentRegistryLayer是用于共享组件注册表和加速测试的层父类。

使用方法

from ftw.testing.layer import ComponentRegistryLayer

class ZCMLLayer(ComponentRegistryLayer):

    def setUp(self):
        super(ZCMLLayer, self).setUp()

        import my.package
        self.load_zcml_file('configure.zcml', my.package)

ZCML_LAYER = ZCMLLayer()

请注意,ComponentRegistryLayer是创建您自己的层(通过继承ComponentRegistryLayer)的基类,不能直接与defaultBases一起使用。这允许我们使用load_zcml_fileload_zcml_string函数。

邮件测试助手

邮件助手对象模拟邮件主机并捕获发送的邮件。然后可以轻松地用于断言。

使用方法

from ftw.testing.mailing import Mailing
import transaction

class MyTest(TestCase):
    layer = MY_FUNCTIONAL_TESTING

 def setUp(self):
     Mailing(self.layer['portal']).set_up()
     transaction.commit()

 def tearDown(self):
     Mailing(self.layer['portal']).tear_down()

 def test_mail_stuff(self):
     portal = self.layer['portal']
     do_send_email()
     mail = Mailing(portal).pop()
     self.assertEquals('Subject: ...', mail)

冻结datetime.now()

在测试依赖于当前时间的代码时,有必要将当前时间设置为特定时间。使用freeze上下文管理器可以非常容易地做到这一点

from ftw.testing import freeze
from datetime import datetime

with freeze(datetime(2014, 5, 7, 12, 30)):
    # test code

freeze上下文管理器修补了datetime模块、time模块并支持Zope的DateTime模块。在退出上下文管理器时移除修补。

更新冻结时间

from ftw.testing import freeze
from datetime import datetime

with freeze(datetime(2014, 5, 7, 12, 30)) as clock:
    # its 2014, 5, 7, 12, 30
    clock.forward(days=2)
    # its 2014, 5, 9, 12, 30
    clock.backward(minutes=15)
    # its 2014, 5, 9, 12, 15

可以忽略模块,这样对该模块中的日期/时间函数的所有调用都将返回实际当前值,而不是冻结值

from ftw.testing import freeze
from datetime import datetime

with freeze(datetime(2014, 5, 7, 12, 30), ignore_modules=['my.package.realtime']):
    pass

可以使用timedelta arguments(https://docs.pythonlang.cn/2/library/datetime.html#datetime.timedelta)进行forwardbackward

静态UUIDS

在断言UUID时,它们在每次测试运行时都会变化可能会很烦人。staticuid装饰器通过使用在作用域内(通常是一个测试用例)前缀和计数的静态UUID来帮助解决这个问题

from ftw.testing import staticuid
from plone.app.testing import PLONE_INTEGRATION_TESTING
from unittest import TestCase

class MyTest(TestCase):
    layer = PLONE_INTEGRATION_TESTING

    @staticuid()
    def test_all_the_things(self):
        doc = self.portal.get(self.portal.invokeFactory('Document', 'the-document'))
        self.assertEquals('testallthethings0000000000000001', IUUID(doc))

    @staticuid('MyUIDS')
    def test_a_prefix_can_be_set(self):
        doc = self.portal.get(self.portal.invokeFactory('Document', 'the-document'))
        self.assertEquals('MyUIDS00000000000000000000000001', IUUID(doc))

通用设置卸载测试

ftw.testing提供了一个用于测试卸载配置文件的测试超类。该测试在安装包之前创建一个Generic Setup快照,然后安装和卸载包,创建另一个快照并比较它。由于卸载配置文件不应包含卸载依赖项,因此不安装其依赖项。

包括适当的测试层设置,测试在一个单独的层上运行,该层不应干扰其他测试。

简单示例

from ftw.testing.genericsetup import GenericSetupUninstallMixin
from ftw.testing.genericsetup import apply_generic_setup_layer
from unittest import TestCase


@apply_generic_setup_layer
class TestGenericSetupUninstall(TestCase, GenericSetupUninstallMixin):
    package = 'my.package'

预期my.package有一个用于安装包的Generic Setup配置文件profile-my.package:default以及一个用于卸载包的profile-my.package:uninstall。预期使用z3c.autoinclude入口点来加载其ZCML。

选项配置为类变量

包的点分名称,作为字符串使用,用于猜测Generic Setup配置文件名称等。这是强制性的。

autoinclude (True)

这使得测试固定化使用为目标plone注册的z3c.autoinclude入口点加载ZCML。

additional_zcml_packages (())

如果需要,可以使用此选项加载ZCML,例如当您需要加载测试zcml时。传入一个包含具有configure.zcml的包的点分名称的可迭代对象。

additional_products (())

要安装的额外Zope产品的列表。

install_profile_name (default)

Generic Setup安装配置文件名称后缀。

skip_files (())

要忽略的通用设置文件的可迭代对象(例如 ("viewlets.xml",))。有时这是必要的,因为并非所有组件都能被正确卸载。例如,不能使用通用设置来删除视图顺序组件 - 但这不是问题,因为当视图/视图管理器不再注册时,它们将不再生效。

完整示例

from ftw.testing.genericsetup import GenericSetupUninstallMixin
from ftw.testing.genericsetup import apply_generic_setup_layer
from unittest import TestCase


@apply_generic_setup_layer
class TestGenericSetupUninstall(TestCase, GenericSetupUninstallMixin):
    package = 'my.package'
    autoinclude = False
    additional_zcml_packages = ('my.package', 'my.package.tests')
    additional_products = ('another.package', )
    install_profile_name = 'default'
    skip_files = ('viewlets.xml', 'rolemap.xml')

禁用quickinstaller快照

Quickinstaller通常在安装每个GS配置文件前后创建一个完整的通用设置(GS)快照,以便之后可以卸载配置文件。

在测试中,我们通常不需要此功能,并希望禁用它以加快测试速度。

ftw.testing.quickinstaller模块提供了一个修补程序,用于替换快速安装事件处理程序以跳过创建快照。我们通常希望在早期(当加载testing.py)时这样做,以便所有测试都能加速。然而,一些涉及快速安装的测试依赖于创建快照(请参阅关于卸载测试的上一节)。因此,快照修补程序对象提供了上下文管理器,用于临时启用/禁用快照功能。

使用方法

尽早禁用快照,以便一切快速运行。通常在模块作用域的testing.py中完成此操作,以便在测试运行器导入测试时已经发生。

from ftw.testing.quickinstaller import snapshots
from plone.app.testing import PloneSandboxLayer

snapshots.disable()

class MyPackageLayer(PloneSandboxLayer):
...

在测试与快速安装快照相关的事项,如卸载时,可以为上下文管理器或通常重新启用快照。

from ftw.testing.quickinstaller import snapshots

snapshots.disable()
# snapshotting is now disabled

with snapshots.enabled():
    # snapshotting is enabled only within this block

snapshots.enable()
# snapshotting is now enabled

with snapshots.disabled():
    # snapshotting is disabled only within this block

事务拦截器

TransactionInterceptor修补Zope的事务管理器,以防止代码与事务交互。

例如,可以用来确保在集成测试层上运行时没有测试提交事务。

拦截器需要手动使用install()进行安装,并在结束时使用uninstall()进行移除。确保正确卸载是用户的责任。

安装拦截器后,它尚未激活并传递所有调用。拦截从intercept()开始,并在调用clear()时结束。

from ftw.testing import TransactionInterceptor

interceptor = TransactionInterceptor().install()
try:
    interceptor.intercept(interceptor.BEGIN | interceptor.COMMIT
                          | interceptor.ABORT)
    # ...
    interceptor.clear()
    transaction.abort()
finally:
    interceptor.uninstall()

测试层

组件注册隔离层

plone.app.testing的默认测试层(如PLONE_FIXTURE)不会为每个测试隔离组件注册表。

ftw.testingCOMPONENT_REGISTRY_ISOLATION测试层为每个测试隔离组件注册表,提供堆叠的ZCML配置上下文,并提供load_zcml_stringload_zcml_file方法来加载ZCML。

示例

# testing.py
from ftw.testing.layer import COMPONENT_REGISTRY_ISOLATION
from plone.app.testing import IntegrationTesting
from plone.app.testing import PloneSandboxLayer
from zope.configuration import xmlconfig


class MyPackageLayer(PloneSandboxLayer):
    defaultBases = (COMPONENT_REGISTRY_ISOLATION,)

    def setUpZope(self, app, configurationContext):
        import my.package
        xmlconfig.file('configure.zcml', ftw.package,
                       context=configurationContext)

MY_PACKAGE_FIXTURE = MyPackageLayer()
MY_PACKAGE_INTEGRATION = IntegrationTesting(
    bases=(MY_PACKAGE_FIXTURE,
           COMPONENT_REGISTRY_ISOLATION),
    name='my.package:integration')


# ----------------------------
# test_*.py
from unittest import TestCase

class TestSomething(TestCase):
    layer = MY_PACKAGE_INTEGRATION

    def test(self):
        self.layer['load_zcml_string']('<configure>...</configure>')

临时目录层

TEMP_DIRECTORY测试层为每个测试创建一个空的临时目录,并在拆卸时递归地删除它。

可以使用temp_directory键访问目录的路径。

使用示例

from unittest import TestCase
from ftw.testing.layer import TEMP_DIRECTORY


class TestSomething(TestCase):
    layer = TEMP_DIRECTORY

    def test(self):
        path = self.layer['temp_directory']

控制台脚本测试层

控制台脚本层有助于测试控制台脚本。在层设置时,它创建并执行一个隔离的buildout,该buildout包含正在开发的包,创建该包的所有控制台脚本。这使得通过实际执行它们来测试控制台脚本变得容易。

使用示例

# testing.py
from ftw.testing.layer import ConsoleScriptLayer

CONSOLE_SCRIPT_TESTING = ConsoleScriptLayer('my.package')


# test_*.py
from my.package.testing import CONSOLE_SCRIPT_TESTING
from unittest import TestCase


class TestConsoleScripts(TestCase):
    layer = CONSOLE_SCRIPT_TESTING

    def test_executing_command(self):
        exitcode, output = self.layer['execute_script']('my-command args')
        self.assertEqual('something\n', output)

请注意,依赖项zc.recipe.egg是构建控制台脚本所必需的。您可以将此依赖项放入您的tests extras require中。

升级

从ftw.testing 1.x升级到2.0

mocker已被unittest.mock取代。这是一个破坏性更改,可能需要修改基于MockTestCase的现有测试。

使用mocker时,在测试中记录期望是在record模式下完成的,而测试中在mock中使用是在replay模式下完成的。在unittest.mock中不再是这样。以下是一个如何采用期望的简单示例:

# Mocking with mocker
mock = self.mocker.mock()  # mocker.Mock
self.expect(mock.lock()).result('already locked')
self.replay()
self.assertEqual(mock.lock(), 'already locked')
# Mocking with unittest.mock
mock = self.mock()  # unittest.mock.Mock
mock.lock.return_value = 'already locked'
self.assertEqual(mock.lock(), 'already locked')

兼容性

支持在 Plone 4.35.15.2 上运行。

变更日志

2.0.7 (2022-12-23)

  • 使 freezer 的 “ignore_modules” 更健壮。[phgross]

2.0.6 (2020-05-28)

  • 通过避免检索完整的调用堆栈,显著优化 freezer 的 “ignore_modules”。[buchi]

2.0.5 (2019-12-17)

  • 停止在 Plone 5.1 上跳过 quickinstall_uninstallation 测试。[djowett-ftw]

  • 在删除 unittest2 后恢复卸载测试的差异。[djowett-ftw]

2.0.4 (2019-12-04)

  • 添加缺失的 getToolByName 默认参数。[Nachtalb]

2.0.3 (2019-11-22)

  • 修复在时间冻结时 datetime 的 pickling。[buchi]

2.0.2 (2019-11-19)

  • 修复 mock_tool 的 super 调用。[buchi]

2.0.1 (2019-11-19)

  • 修复 MockTestCase 中的组件注册拆解。[buchi]

  • 将 FrozenDateTime 类重命名为 FrozenDatetime 以解决 plone.event 中 Zope DateTime 的检查问题。[buchi]

2.0.0 (2019-11-11)

  • 添加对 Plone 5.2 和 Python 3 的支持。[buchi]

  • 不再基于 plone.mocktestcase,因为它与 Python 3 不兼容,并使用 unittest.mock 替换 mocker。这是一个 破坏性 改变,可能需要修改基于 MockTestCase 的现有测试。[buchi]

  • 使用 forbiddenfruit 和 mocker 重新实现 freezer。[buchi]

1.20.1 (2019-04-04)

  • 优化 “ignore_modules” 以避免不必要的堆栈检查。[Rotonen]

1.20.0 (2019-01-25)

  • 将 “ignore_modules” 支持添加到 freezer。[jone]

1.19.2 (2018-11-05)

  • 修复 Zope DateTime 在时区感知冻结中的问题。[njohner]

1.19.1 (2018-10-23)

  • 修复 README.rst 中的无效 reST。[Nachtalb]

1.19.0 (2018-10-15)

  • 停止对 plone 4.2 的支持。[jone]

  • 修复获取时区感知 “now” 的问题。[njohner]

1.18.0 (2018-07-12)

  • 扩展 staticuid 以使其也成为上下文管理器。[jone]

  • 同时冻结 datetime.utcnow()。[Rotonen]

1.17.0 (2017-10-02)

  • 添加 FTWIntegrationTestingFTWIntegrationTestCase。[jone]

1.16.0 (2017-08-08)

  • 支持 ConsoleScriptLayer 的 Plone 5.1。[jone]

1.15.2 (2017-07-18)

  • Freezer:在向前/向后移动时钟时保持时区信息。[jone]

  • Freezer:修复 today() 和 time() 中的 DST-bug。[jone]

1.15.1 (2017-07-04)

  • 修复 savepoint 模拟以清理 savepoints。[jone]

1.15.0 (2017-07-03)

  • 向事务拦截器添加 savepoint 模拟。[jone]

1.14.0 (2017-06-23)

  • 不再需要 “Plone” egg。[jone]

1.13.0 (2017-06-20)

  • 添加事务拦截器。[jone]

1.12.0 (2017-06-19)

  • 支持 Plone 5.1。[mathias.leimgruber, jone]

  • 删除 splinter 浏览器。使用 ftw.testbrowser 代替。[mathias.leimgruber, jone]

  • 停止对 plone 4.1 的支持。[jone]

1.11.0 (2016-03-31)

  • Freezer:使用 forbiddenfruit 重新实现 “now” 的补丁。这解决了冻结 datetime 对象的 pickling 和比较的问题。[jone]

1.10.3 (2015-10-11)

  • Freezer:在提交到数据库时禁用冻结以防止 pickling 错误。[jone]

  • Freezer bugfix:在离开 freeze 上下文管理器时替换 datetime 实例。[jone]

1.10.2 (2015-07-30)

  • 为 “freeze” 添加 timezone(tz) 支持。[phgross]

1.10.1 (2015-07-27)

  • 使用 “now” 作为 “freeze” 的默认值。[jone]

1.10.0 (2015-05-18)

  • 使用 forwardbackward 更新冻结时间。[jone]

1.9.1 (2015-05-15)

  • 修复 staticuid 装饰方法中的 site hook。[jone]

1.9.0 (2015-05-15)

  • 添加 staticuid 装饰器以获取静态 uids。[jone]

1.8.1 (2015-01-05)

  • 声明 COMPONENT_REGISTRY_ISOLATION 层所需的 p.a.testing 的缺失依赖。[jone]

1.8.0 (2014-12-31)

  • 实现控制台脚本测试层。[jone]

  • 实现 TEMP_DIRECTORY 测试层。[jone]

  • 实现 COMPONENT_REGISTRY_ISOLATION 层。[jone]

1.7.0 (2014-09-30)

  • 添加禁用测试中 quickinstaller snapphotting 的补丁器。[jone]

1.6.4 (2014-05-01)

  • 通用设置卸载测试:添加第二个测试,该测试使用 Portal Setup 进行卸载。这确保 Portal Setup 卸载与 quickinstaller 卸载行为相同。[deif]

1.6.3 (2014-04-30)

  • 通用设置卸载测试:移除is_product选项,因为我们需要的卸载外部方法要求包必须是产品。 [jone]

  • 通用设置卸载测试:测试是否存在卸载外部方法。今天,卸载外部方法对于正确卸载包仍然是必要的。 [jone]

1.6.2 (2014-04-30)

  • 通用设置测试:使用quickinstaller进行卸载。 [jone]

1.6.1 (2014-04-29)

  • 在创建快照之前先安装配置文件依赖项。 [deif]

1.6.0 (2014-04-29)

  • 实现通用设置卸载基本测试。 [jone]

1.5.2 (2014-02-09)

  • 修复在freeze上下文中调用isinstance时的freezed时间问题。 [jone]

1.5.1 (2014-02-08)

  • 实现用于冻结时间的freeze上下文管理器。 [jone]

1.5.0 (2013-09-24)

  • AT表单页面对象:添加用于测试可见模式和字段的schemata辅助方法。 [jone]

1.4 (2013-08-26)

  • 添加自定义mailhost类,分别记住每封电子邮件的发送者和接收者。 [deif]

  • 弃用@javascript,因为Selenium与PhantomJS太不稳定。移除测试和文档,@javascript装饰器目前仍然有效,但需要从ftw.testing.browser导入。 [jone]

  • 页面对象:添加Plone.visit(obj)函数。 [jone]

  • 修复了一个罕见的bug,其中MockMailHost消息列表被另一个实例替换。 [jone, deif]

1.3.1 (2013-05-24)

  • Mailing辅助类移动到其自己的模块mailing。 [deif]

1.3 (2013-05-03)

  • 停止官方支持Plone 4.0。 [jone]

  • 组件注册层:使用隔离的ZCML层。当使用相同的层实例时,可能与其他集成或功能测试层冲突。 [jone]

  • 添加splinter集成和Plone页面对象。 [jone]

  • onegov.ch批准:在readme中添加徽章。 [jone]

  • MockTestCase:支持在模拟getToolByName时使用Products.PloneHotfix20121106补丁。 [jone]

  • MockTestCase:添加检查setUp是否被正确调用的检查。 [jone]

1.2 (2012-05-22)

  • stub_reponse方法添加到MockTestCase,并调整stub_request方法。 [phgross]

  • 使stub_request方法的可配置性,以便提供接口。 [phgross]

  • 使stub_request方法也模拟响应的getStatus。 [phgross]

  • stub_request方法添加到MockTestCase。 [jone]

  • 在模拟测试案例中不再拆卸组件注册。使用ComponentRegistryLayer。 [jone]

  • 添加ComponentRegistryLayer基类。 [jone]

  • 向MockTestCase添加mock_interfacestub_interface方法,创建模拟并使用接口作为规范。 [jone]

  • 在创建模拟或存根提供接口时,也可以直接接受接口而不是接口列表。 [jone]

1.1 (2011-11-16)

  • 修补mock_tool:不计数,以便可以多次使用。 [jone]

1.0 (2011-10-12)

  • 初始版本

项目详情


下载文件

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

源代码分布

ftw.testing-2.0.7.tar.gz (59.0 kB 查看哈希值)

上传时间: 源代码

由以下支持