跳转到主要内容

基于plone.testing的Plone应用程序的测试工具。

项目描述

简介

plone.app.testing 提供了为在 Plone 上运行的代码编写集成和功能测试的工具。它基于 plone.testing。如果您不熟悉 plone.testing、层的概念或 zope.testing 测试运行器,请查看 plone.testing 文档。实际上,即使您仅与 Plone 合作,您也可能希望使用其一些功能进行单元测试。

简而言之,plone.app.testing 包括

  • 一组层,用于设置包含 Plone 站点的固定装置,旨在编写集成和功能测试。

  • 一组辅助函数,其中一些用于编写您自己的层,一些适用于测试本身。

  • 一个方便的层基类,扩展 plone.testing.Layer,这使得编写扩展 Plone 站点固定装置的自定义层变得更容易,具有适当的隔离和拆除。

  • zope.testing.cleanup 的清理钩子,用于清理 Plone 安装中找到的全局状态。这对于单元测试很有用。

兼容性

plone.app.testing 5.x 与 Plone 5 兼容。 plone.app.testing 4.x 与 Plone 4 和 Zope 2.12 兼容。它可能与其他版本兼容,但不与早期版本兼容。对于 Plone 3 和 Zope 2.10,请使用 plone.app.testing 3.x。

安装和使用

要在自己的包中使用 plone.app.testing,您需要将其作为依赖项添加。大多数人喜欢将仅用于测试的依赖项分开,这样它们就不需要在不会运行测试的场景(如生产服务器)中安装。这可以通过使用 test 额外功能实现。

setup.py 中,添加或修改 extras_require 选项,如下所示

extras_require = {
    'test': [
            'plone.app.testing',
        ]
},

这将包括 plone.testing,带有 [z2][zca][zodb] 额外功能(这是 plone.app.testing 自身所依赖的)。

有关如何将测试运行器添加到 buildout 以及如何编写和运行测试的更多详细信息,请参阅 plone.testing 文档。

层参考

此包包含一个层类,plone.app.testing.layers.PloneFixture,它设置 Plone 网站固定装置。它与来自 plone.testing 的其他层结合使用,以提供多个层实例。重要的是要意识到,这些层都具有相同的根本固定装置:它们只是管理测试设置和拆解的方式不同。

设置后,固定装置将

  • 通过堆叠 DemoStorage 创建 ZODB 沙盒。这确保在层设置期间做出的持久更改可以干净地拆解。

  • 配置全局组件注册沙盒。这确保全局组件注册(例如,由于加载 ZCML 配置的结果)可以干净地拆解。

  • 使用具有 disable-autoinclude 功能集的配置上下文。这会导致 Plone 不自动加载使用 z3c.autoinclude.plugin:plone 入口点通过 z3c.autoinclude 安装的任何已安装包的配置。(这是为了避免意外污染测试固定装置 - 自定义层应根据需要显式加载包的 ZCML 配置)。

  • 安装 Plone 依赖的多个 Zope 2 风格的产品。

  • 加载这些产品的 ZCML 以及 Products.CMFPlone 的 ZCML,这反过来又拉入 Plone 核心的配置。

  • 创建一个默认的 Plone 网站,启用默认主题,但没有默认内容。

  • 向根用户文件夹添加具有 Manager 角色的用户。

  • 向此实例添加具有 Member 角色的测试用户。

对于每个测试

  • 测试用户已登录

  • 设置本地组件网站

  • 清理各种全局缓存

在模块 plone.app.testing.interfaces 中定义了各种常量来描述此环境

常量

目的

PLONE_SITE_ID

在 Zope 应用程序根内部 Plone 网站对象的 ID。

PLONE_SITE_TITLE

Plone 网站的标题

DEFAULT_LANGUAGE

Plone 网站的默认语言('en')

TEST_USER_ID

测试用户的 ID

TEST_USER_NAME

测试用户的用户名

TEST_USER_PASSWORD

测试用户的密码

TEST_USER_ROLES

测试用户的默认全局角色 - ('Member',)

SITE_OWNER_NAME

拥有 Plone 网站的用户的用户名。

SITE_OWNER_PASSWORD

拥有 Plone 网站的用户的密码。

除了其基本层提供的资源外,所有层也公开了在测试期间可用的资源。

门户

Plone 网站根目录。

Plone站点固定装置

plone.app.testing.PLONE_FIXTURE

plone.app.testing.layers.PloneFixture

基类

plone.testing.z2.STARTUP

资源

此层在 z2.STARTUP 固定层之上设置 Plone 网站固定层。

不应直接使用此层,因为它不提供任何测试生命周期或事务管理。相反,您应使用以下内容中概述的由 IntegrationTestingFunctionalTesting 类创建的层。

Mock MailHost

plone.app.testing.MOCK_MAILHOST_FIXTURE

plone.app.testing.layers.MockMailHostLayer

基类

plone.app.testing.layers.PLONE_FIXTURE

资源

此层在 PLONE_FIXTURE 之上构建,以修补 Plone 的 MailHost 实现。

使用它,任何尝试发送电子邮件的操作都会将每封电子邮件作为字符串存储在 portal.MailHost.messages 列表中的字符串。

不应直接使用此层,因为它不提供任何测试生命周期或事务管理。相反,您应使用像下面这样的由 IntegrationTestingFunctionalTesting 类创建的层。

from plone.app.testing import MOCK_MAILHOST_FIXTURE

MY_INTEGRATION_TESTING = IntegrationTesting(
    bases=(
        MY_FIXTURE,
        MOCK_MAILHOST_FIXTURE,
    ),
    name="MyFixture:Integration"
)

PloneWithPackageLayer 类

大多数附加组件不需要比加载 ZCML 文件和运行通用设置配置文件更多的设置。

使用此辅助类,可以轻松地实例化固定层

from plone.app.testing import PloneWithPackageLayer
import my.addon

FIXTURE = PloneWithPackageLayer(
    zcml_package=my.addon,
    zcml_filename='configure.zcml',
    gs_profile_id='my.addon:default',
    name="MyAddonFixture"
)

PloneWithPackageLayer 构造函数还接受两个其他关键字参数: basesadditional_z2_products

bases 参数接受一系列基本层固定层。出于其他原因,它还有用,例如,传递一个调用 plone.app.testing API 的固定层。这种需求可能在开发过程中出现。

additional_z2_products 参数接受一系列需要作为 Zope2 产品安装并作为测试附加组件依赖项的包名。

集成和功能测试测试生命周期

plone.app.testing 包含两个层类,IntegrationTestingFunctionalTesting,它们是从 plone.testing.z2 中的相应层类派生出来的。

这些类设置 apprequestportal 资源,并在每次测试运行之间重置固定层(包括各种全局缓存)。

plone.testing 中的类一样,IntegrationTesting 类将为每个测试创建一个新事务,并在测试拆除时回滚,这对于集成测试来说是高效的,而 FunctionalTesting 将为每个测试创建一个堆叠的 DemoStorage,并在测试拆除时弹出,这使得可以执行显式提交的代码(例如,通过使用 zope.testbrowser 的测试)。

在创建自定义固定层时,通常的模式是创建一个新的层类,该类以 PLONE_FIXTURE 作为其默认基类,将其作为单独的“固定层”实例化。这个层不应直接用于测试,因为它没有测试/事务生命周期管理,但它代表了一个共享的固定层,可能用于功能测试和集成测试。它也是遵循相同模式的其他层的扩展点。

一旦定义了此固定层,就可以使用 IntegrationTestingFunctionalTesting 类定义“最终用户”层。例如

from plone.testing import Layer
from plone.app.testing import PLONE_FIXTURE
from plone.app.testing import IntegrationTesting, FunctionalTesting

class MyFixture(Layer):
    defaultBases = (PLONE_FIXTURE,)

    ...

MY_FIXTURE = MyFixture()

MY_INTEGRATION_TESTING = IntegrationTesting(bases=(MY_FIXTURE,), name="MyFixture:Integration")
MY_FUNCTIONAL_TESTING = FunctionalTesting(bases=(MY_FIXTURE,), name="MyFixture:Functional")

下面是 PloneSandboxLayer 层的更全面的示例。

Plone集成测试

plone.app.testing.PLONE_INTEGRATION_TESTING

plone.app.testing.layers.IntegrationTesting

基类

plone.app.testing.PLONE_FIXTURE

资源

portal(仅测试设置)

此层可用于针对基本 PLONE_FIXTURE 层进行集成测试。

如果不需要设置任何其他共享配置,您可以直接在测试中使用此功能。

然而,通常情况下,您不会扩展这一层 - 参见上文。

Plone功能测试

plone.app.testing.PLONE_FUNCTIONAL_TESTING

plone.app.testing.layers.FunctionalTesting

基类

plone.app.testing.PLONE_FIXTURE

资源

portal(仅测试设置)

此层可用于对基本PLONE_FIXTURE层进行功能测试,例如使用zope.testbrowser

如果不需要设置任何其他共享配置,您可以直接在测试中使用此功能。

再次提醒,通常情况下您不会扩展这一层 - 参见上文。

Plone ZServer

plone.app.testing.PLONE_ZSERVER

plone.testing.z2.ZServer

基类

plone.app.testing.PLONE_FUNCTIONAL_TESTING

资源

portal(仅测试设置)

此层旨在用于通过实时运行的HTTP服务器进行功能测试,例如使用Selenium或Windmill。

再次提醒,通常情况下您不会扩展这一层。若要创建具有运行中ZServer的自定义层,您可以使用与该层相同的模式,例如:

from plone.testing import Layer
from plone.testing import z2
from plone.app.testing import PLONE_FIXTURE
from plone.app.testing import FunctionalTesting

class MyFixture(Layer):
    defaultBases = (PLONE_FIXTURE,)

    ...

MY_FIXTURE = MyFixture()
MY_ZSERVER = FunctionalTesting(bases=(MY_FIXTURE, z2.ZSERVER_FIXTURE), name='MyFixture:ZServer')

有关z2.ZSERVER层的详细信息,请参阅plone.testing中的描述。

Plone FTP服务器

plone.app.testing.PLONE_FTP_SERVER

plone.app.testing.layers.FunctionalTesting

基类

plone.app.testing.PLONE_FIXTURE plone.testing.z2.ZSERVER_FIXTURE

资源

portal(仅测试设置)

此层旨在用于通过实时FTP服务器进行功能测试。

它与PLONE_ZSERVER层在语义上是等价的。

有关z2.FTP_SERVER层的详细信息,请参阅plone.testing中的描述。

辅助函数

提供了一些辅助函数,用于在测试和自定义层中使用。

Plone站点上下文管理器

ploneSite(db=None, connection=None, environ=None)

使用此上下文管理器在层设置期间访问和修改Plone站点。在大多数情况下,您将不带参数使用它,但如果您有特殊需求,您可以将它绑定到特定的数据库实例。有关详细信息,请参阅plone.testingzopeApp()上下文管理器的描述(此上下文管理器内部使用)。

通常的模式是在您自己的层的setUp()tearDown()中调用它。

from plone.testing import Layer
from plone.app.testing import ploneSite

class MyLayer(Layer):

    def setUp(self):

        ...

        with ploneSite() as portal:

            # perform operations on the portal, e.g.
            portal.title = u"New title"

在此,portal是Plone站点的根。在进入with块之前开始一个事务,并在退出块时提交,除非抛出异常,在这种情况下,将回滚。

在块内部,本地组件站点设置为Plone站点根,以便本地组件查找应该正常工作。

警告:不要在ploneSite块中尝试加载ZCML文件。因为本地站点设置为Plone站点,您可能会意外地在本地站点管理器中注册组件,这可能导致后续的序列化错误。

注意:您不应在测试中使用此功能,或在基于此包中某一层的层的testSetUp()testTearDown()方法中使用。请使用portal资源。

此外注意:如果您正在编写设置Plone站点配置文件的层,您可能希望使用PloneSandboxLayer层基类,并实现setUpZope()setUpPloneSite()tearDownZope()和/或tearDownPloneSite()方法。详见下文。

用户管理

login(portal, userName)

模拟以指定用户登录。这是基于plone.testing中的z2.login()辅助程序,但您不是传递特定的用户文件夹,而是传递门户(例如,通过portal层资源获取)。

例如

import unittest2 as unittest

from plone.app.testing import PLONE_INTEGRATION_TESTING
from plone.app.testing import TEST_USER_NAME
from plone.app.testing import login

...

class MyTest(unittest.TestCase):

    layer = PLONE_INTEGRATION_TESTING

    def test_something(self):
        portal = self.layer['portal']
        login(portal, TEST_USER_NAME)

        ...
logout()

模拟注销,即成为匿名用户。这相当于 plone.testing 中的 z2.logout() 辅助函数。

例如

import unittest2 as unittest

from plone.app.testing import PLONE_INTEGRATION_TESTING
from plone.app.testing import logout

...

class MyTest(unittest.TestCase):

    layer = PLONE_INTEGRATION_TESTING

    def test_something(self):
        portal = self.layer['portal']
        logout()

        ...
setRoles(portal, userId, roles)

设置指定用户的角色。roles 是一个角色列表。

例如

import unittest2 as unittest

from plone.app.testing import PLONE_INTEGRATION_TESTING
from plone.app.testing import TEST_USER_ID
from plone.app.testing import setRoles

...

class MyTest(unittest.TestCase):

    layer = PLONE_INTEGRATION_TESTING

    def test_something(self):
        portal = self.layer['portal']
        setRoles(portal, TEST_USER_ID, ['Manager'])

产品和配置文件安装

applyProfile(portal, profileName, blacklisted_steps=None)

通过 portal_setup 工具按名称安装一个 GenericSetup 配置文件(通常是一个扩展配置文件)。名称通常由一个包名和一个配置文件名组成。不要使用 profile- 前缀。

例如

from plone.testing import Layer

from plone.app.testing import ploneSite
from plone.app.testing import applyProfile

...

class MyLayer(Layer):

    ...

    def setUp(self):

        ...

        with ploneSite() as portal:
            applyProfile(portal, 'my.product:default')

            ...
quickInstallProduct(portal, productName, reinstall=False)

使用此函数将特定产品安装到指定的 Plone 站点,使用附加组件控制面板代码(portal setup)。如果 reinstallFalse 且产品已安装,则不会发生任何操作。如果 reinstall 为 true,如果产品已安装,则执行卸载和安装。 productName 应该是一个完整的点分隔名称,例如 Products.MyProductmy.product

例如

from plone.testing import Layer

from plone.app.testing import ploneSite
from plone.app.testing import quickInstallProduct

...

class MyLayer(Layer):

    ...

    def setUp(self):

        ...

        with ploneSite() as portal:
            quickInstallProduct(portal, 'my.product')

            ...

组件架构沙盒化

pushGlobalRegistry(portal, new=None, name=None)

创建或获取一个全局组件注册表的堆栈,并将一个新的注册表推送到堆栈顶部。这允许有效地拆除 Zope 组件架构注册(例如通过 ZCML 加载)。

如果您要使用此函数,请阅读 plone.testing 中相应的 zca.pushGlobalRegistry() 文档。特别是,请注意您必须相互调用 popGlobalRegistry()(见下文)。

此辅助函数基于 zca.pushGlobalRegistry(),但还将修复 Plone 站点 portal 中的本地组件注册表,使其具有正确的基。

例如

from plone.testing import Layer

from plone.app.testing import ploneSite
from plone.app.testing import pushGlobalRegistry
from plone.app.testing import popGlobalRegistry

...

class MyLayer(Layer):

    ...

    def setUp(self):

        ...

        with ploneSite() as portal:
            pushGlobalRegistry(portal)

            ...
popGlobalRegistry(portal)

拆毁由 pushGlobalRegistry() 创建的组件架构堆栈顶部。

例如

...

    def tearDown(self):

        with ploneSite() as portal:
            popGlobalRegistry(portal)

全局状态清理

tearDownMultiPluginRegistration(pluginName)

可插拔认证服务(PluggableAuthService)的“多插件”保存在全局注册表中。如果您已注册了一个插件,例如使用 registerMultiPlugin() API,则应在您的层 tearDown() 方法中拆除该注册。您可以使用此辅助函数,并传递一个插件名。

例如

from plone.testing import Layer

from plone.app.testing import ploneSite
from plone.app.testing import tearDownMultiPluginRegistration

...

class MyLayer(Layer):

    ...

    def tearDown(self):

        tearDownMultiPluginRegistration('MyPlugin')

        ...

层基类

如果您正在编写用于测试自己的 Plone 扩展产品的自定义层,您通常希望在设置时执行以下操作

  1. 在基础层的一个 DemoStorage 之上堆叠一个新的。这确保了在层设置期间执行的任何持久更改都可以通过弹出演示存储完全拆毁。

  2. 堆叠一个新的 ZCML 配置上下文。这将分离加载的 ZCML 文件的信息,以防其他独立层在当前层拆毁后希望加载相同的文件。

  3. 推送一个新的全局组件注册表。这允许您注册组件(例如通过加载 ZCML 或使用来自 zope.component 的测试 API)并通过弹出组件注册表轻松拆卸这些注册。

  4. 加载产品的 ZCML 配置

  5. 将产品安装到测试固定 Plone 站点

当然,您可能还希望进行其他更改,例如创建一些基本内容或更改一些设置。

在拆毁时,您将想要

  1. 移除在设置期间添加到全局注册表的任何可插拔认证服务“多插件”。

  2. 弹出全局组件注册表以注销通过 ZCML 加载的组件。

  3. 弹出配置上下文资源以恢复其状态。

  4. 弹出 DemoStorage 以撤销任何持久更改。

如果您在设置过程中进行了其他更改,而这些更改不包含在本广泛的拆卸中,您也需要在这里明确地拆卸这些更改。

将演示存储和组件注册堆叠起来是避免测试之间固件泄露的最安全方法。然而,确保所有事情按照正确的顺序进行可能会有点复杂。

为了让事情更简单,您可以使用 PloneSandboxLayer 基类层。它扩展了 plone.testing.Layer 并为您实现了 setUp()tearDown()。您只需重写以下方法之一

setUpZope(self, app, configurationContext)

这个方法在设置期间被调用。 app 是 Zope 应用程序的根。 configurationContext 是一个新的 ZCML 配置上下文。您可以使用它来加载 ZCML,使用辅助函数 plone.testing.z2.installProduct() 安装产品,或操纵其他全局状态。

setUpPloneSite(self, portal)

这个方法在设置期间被调用。 portal 是由 ploneSite() 上下文管理器配置的 Plone 站点根。您可以使用它来在 Plone 站点内部进行持久性更改,例如使用 applyProfile()quickInstallProduct() 辅助函数安装产品,或设置默认内容。

tearDownZope(self, app)

这个方法在拆卸期间被调用,在全局组件注册和堆叠的 DemoStorage 被弹出之前。您可以使用它来拆卸任何额外的全局状态。

注意:全局组件注册 PAS 多插件注册将自动拆卸。产品安装不会,因此如果您在 setUpZope() 期间安装了任何产品,您应该使用 uninstallProduct() 辅助函数。

tearDownPloneSite(self, portal)

这个方法在拆卸期间被调用,在全局组件注册和堆叠的 DemoStorage 被弹出之前。在这个方法中,本地组件站点钩子被设置,让您可以访问本地组件。

注意:由于堆叠的 DemoStorage,ZODB 的持久性更改将自动拆卸。因此,此方法不如此处描述的其他方法常用。

让我们展示这样一个层可能看起来更全面的一个例子。想象我们有一个产品 my.product。它有一个 configure.zcml 文件,该文件加载一些组件并注册一个 GenericSetup 配置文件,使其可安装在 Plone 站点上。在层设置期间,我们希望加载产品的配置并将其安装到 Plone 站点上。

该层通常位于包根目录下的一个模块 testing.py 中,即 my.product.testing

from plone.app.testing import PloneSandboxLayer
from plone.app.testing import PLONE_FIXTURE
from plone.app.testing import IntegrationTesting

from plone.testing import z2

class MyProduct(PloneSandboxLayer):

    defaultBases = (PLONE_FIXTURE,)

    def setUpZope(self, app, configurationContext):
        # Load ZCML
        import my.product
        self.loadZCML(package=my.product)

        # Install product and call its initialize() function
        z2.installProduct(app, 'my.product')

        # Note: you can skip this if my.product is not a Zope 2-style
        # product, i.e. it is not in the Products.* namespace and it
        # does not have a <five:registerPackage /> directive in its
        # configure.zcml.

    def setUpPloneSite(self, portal):
        # Install into Plone site using portal_setup
        self.applyProfile(portal, 'my.product:default')

    def tearDownZope(self, app):
        # Uninstall product
        z2.uninstallProduct(app, 'my.product')

        # Note: Again, you can skip this if my.product is not a Zope 2-
        # style product

MY_PRODUCT_FIXTURE = MyProduct()
MY_PRODUCT_INTEGRATION_TESTING = IntegrationTesting(bases=(MY_PRODUCT_FIXTURE,), name="MyProduct:Integration")

在这里,MY_PRODUCT_FIXTURE 是“固件”基础层。其他层可以使用此作为基础,如果它们想在固件上构建,但它们不会直接用于测试。为此,我们创建了一个 IntegrationTesting 实例,MY_PRODUCT_INTEGRATION_TESTING

当然,我们也可以创建一个 FunctionalTesting 实例,例如

MY_PRODUCT_FUNCTIONAL_TESTING = FunctionalTesting(bases=(MY_PRODUCT_FIXTURE,), name="MyProduct:Functional")

当然,我们可以在层设置中做很多事情。例如,假设产品有一个内容类型 'my.product.page',我们想要创建一些测试内容。我们可以这样做

from plone.app.testing import TEST_USER_ID
from plone.app.testing import TEST_USER_NAME
from plone.app.testing import login
from plone.app.testing import setRoles

...

    def setUpPloneSite(self, portal):

        ...

        setRoles(portal, TEST_USER_ID, ['Manager'])
        login(portal, TEST_USER_NAME)
        portal.invokeFactory('my.product.page', 'page-1', title=u"Page 1")
        setRoles(portal, TEST_USER_ID, ['Member'])

...

请注意,与测试不同,在层设置时间没有用户登录,所以我们必须显式地将测试用户登录。在这里,我们还临时授予测试用户 Manager 角色,以允许对象构建(这会执行显式的权限检查)。

注意:对于上述所有测试设置,自动拆除就足够了。如果在层设置期间进行的唯一更改是持久、ZODB数据或全局组件注册,则不需要额外的拆除。对于任何其他管理的全局状态,您应该编写一个 tearDownPloneSite() 方法来执行必要的清理。

给定这个层,我们可以编写一个测试(例如在 tests.py 中):

import unittest2 as unittest
from my.product.testing import MY_PRODUCT_INTEGRATION_TESTING

class IntegrationTest(unittest.TestCase):

    layer = MY_PRODUCT_INTEGRATION_TESTING

    def test_page_dublin_core_title(self):
        portal = self.layer['portal']

        page1 = portal['page-1']
        page1.title = u"Some title"

        self.assertEqual(page1.Title(), u"Some title")

有关如何编写和运行测试和断言的更多信息,请参阅 plone.testing

常见测试模式

plone.testing 的文档包含了编写各种测试的基本技术的详细信息。然而,在 Plone 的上下文中,某些模式往往会反复出现。下面,我们将通过简短的代码示例来尝试编制一些更常用的模式。

本节中的示例均旨在用于测试。其中一些也可能在层设置/拆除中很有用。我们在这里使用了 unittest 语法,尽管这些示例也可以很容易地采用 doctests。

我们假设您正在使用一个以 PLONE_FIXTURE 为基础的层(无论是直接还是间接),并使用上面所示的 IntegrationTestingFunctionalTesting 类。

我们还将假设变量 appportalrequest 来自相对层资源,例如,使用以下方法:

app = self.layer['app']
portal = self.layer['portal']
request = self.layer['request']

请注意,在使用 plone.testing 中的 layered() 函数设置的 doctest 中,layer 位于全局命名空间中,因此您会这样做,例如 portal = layer['portal']

需要导入的地方,它们将与代码示例一起显示。如果给定的导入或变量在同一个部分中使用了多次,它只会显示一次。

基本内容管理

在门户根目录下创建一个 ID 为 'f1' 的 'Folder' 类型的内容项

portal.invokeFactory('Folder', 'f1', title=u"Folder 1")

title 参数是可选的。还可以设置其他基本属性,如 description

请注意,这可能会因为 Unauthorized 异常而失败,因为测试用户通常没有权限在门户根目录中添加内容,而 invokeFactory() 方法执行显式的安全检查。您可以设置测试用户的角色以确保他具有必要的权限

from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID

setRoles(portal, TEST_USER_ID, ['Manager'])
portal.invokeFactory('Folder', 'f1', title=u"Folder 1")

要获取此对象,请使用其父对象中的 acquisition 包装

f1 = portal['f1']

要对此对象的属性或方法进行断言

self.assertEqual(f1.Title(), u"Folder 1")

要修改此对象

f1.setTitle(u"Some title")

要在 f1 文件夹内部添加另一个项

f1.invokeFactory('Document', 'd1', title=u"Document 1")
d1 = f1['d1']

要检查一个对象是否在一个容器中

self.assertTrue('f1' in portal)

要从容器中删除一个对象

del portal['f1']

默认情况下,没有安装内容或工作流。您可以启用工作流

portal.portal_workflow.setDefaultChain("simple_publication_workflow")

搜索

获取 portal_catalog 工具

from Products.CMFCore.utils import getToolByName

catalog = getToolByName(portal, 'portal_catalog')

搜索目录

results = catalog(portal_type="Document")

关键字参数是搜索参数。结果是懒列表。您可以调用 len() 来获取搜索结果的数量,或者遍历它。列表中的项是目录大脑。它们具有与目录配置的“元数据”列相对应的属性,例如 TitleDescription 等。请注意,这些是简单属性(不是方法),并包含在对象编入目录时相应属性或方法的值(即它们不一定是最新的)。

要对搜索结果进行断言

self.assertEqual(len(results), 1)

# Copy the list into memory so that we can use [] notation
results = list(results)

# Check the first (and in this case only) result in the list
self.assertEqual(results[0].Title, u"Document 1")

要获取搜索结果中给定项的路径

self.assertEqual(results[0].getPath(), portal.absolute_url_path() + '/f1/d1')

要获取绝对 URL

self.assertEqual(results[0].getURL(), portal.absolute_url() + '/f1/d1')

要获取原始对象

obj = results[0].getObject()

要重新索引对象 d1,以便其目录信息是最新的

d1.reindexObject()

用户管理

要创建一个新用户

from Products.CMFCore.utils import getToolByName

acl_users = getToolByName(portal, 'acl_users')

acl_users.userFolderAddUser('user1', 'secret', ['Member'], [])

参数包括用户名(也将是用户ID)、密码、角色列表和域名列表(很少使用)。

要在集成测试环境中激活特定用户(登录),请使用login方法并传递用户名

from plone.app.testing import login

login(portal, 'user1')

注销(匿名)

from plone.app.testing import logout

logout()

获取当前用户

from AccessControl import getSecurityManager

user = getSecurityManager().getUser()

按名称获取用户

user = acl_users.getUser('user1')

或按用户ID(id和用户名通常是相同的,但在实际场景中也可能不同)

user = acl_users.getUserById('user1')

获取用户的用户名

userName = user.getUserName()

获取用户的ID

userId = user.getId()

权限和角色

在特定上下文中获取用户的角色(考虑本地角色)

from AccessControl import getSecurityManager

user = getSecurityManager().getUser()

self.assertEqual(user.getRolesInContext(portal), ['Member'])

更改测试用户的角色

from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID

setRoles(portal, TEST_USER_ID, ['Member', 'Manager'])

传递不同的用户名以更改其他用户的角色。

在文件夹f1中授予用户本地角色

f1.manage_setLocalRoles(TEST_USER_ID, ('Reviewer',))

检查文件夹‘f1’中给定用户的本地角色

self.assertEqual(f1.get_local_roles_for_userid(TEST_USER_ID), ('Reviewer',))

在门户根目录中授予“成员”和“经理”角色的“查看”权限,而无需从其父级获取额外角色

portal.manage_permission('View', ['Member', 'Manager'], acquire=False)

此方法也可以在文件夹或单个内容项上调用。

断言在门户上下文中具有“查看”权限的角色

roles = [r['name'] for r in portal.rolesOfPermission('View') if r['selected']]
self.assertEqual(roles, ['Member', 'Manager'])

断言在门户上下文中授予“审阅员”角色的权限

permissions = [p['name'] for p in portal.permissionsOfRole('Reviewer') if p['selected']]
self.assertTrue('Review portal content' in permissions)

添加新角色

portal._addRole('Tester')

现在可以将其分配给全球用户(使用setRoles辅助函数)或本地(使用manage_setLocalRoles())。

断言给定上下文中的可用角色

self.assertTrue('Tester' in portal.valid_roles())

工作流

设置默认工作流链

from Products.CMFCore.utils import getToolByName

workflowTool = getToolByName(portal, 'portal_workflow')

workflowTool.setDefaultChain('my_workflow')

在Plone中,大多数链只包含一个工作流,但portal_workflow工具支持更长的链,其中项同时受多个工作流的约束。

要设置多工作流链,用逗号分隔工作流名称。

获取默认工作流链

self.assertEqual(workflowTool.getDefaultChain(), ('my_workflow',))

设置“文档”类型的工工作流链

workflowTool.setChainForPortalTypes(('Document',), 'my_workflow')

您可以传递多个类型名称以一次设置多个链。要设置多工作流链,用逗号分隔工作流名称。要表示类型应使用默认工作流,请使用特殊链名称“(默认)”。

获取门户类型“文档”的工工作流链

chains = dict(workflowTool.listChainOverrides())
defaultChain = workflowTool.getDefaultChain()
documentChain = chains.get('Document', defaultChain)

self.assertEqual(documentChain, ('my_other_workflow',))

获取内容对象f1的当前工工作流链

self.assertEqual(workflowTool.getChainFor(f1), ('my_workflow',))

在更改工作流后更新所有权限

workflowTool.updateRoleMappings()

通过调用事务“发布”来更改内容对象f1的工工作流状态

workflowTool.doActionFor(f1, 'publish')

请注意,这执行了显式的权限检查,因此如果当前用户没有权限执行此工作流操作,您可能会收到一个错误,表明该操作不可用。如果是这样,请使用login()setRoles()来确保当前用户能够更改工作流状态。

检查内容对象f1的当前工工作流状态

self.assertEqual(workflowTool.getInfoFor(f1, 'review_state'), 'published')

属性

设置门户根目录上属性的值

portal._setPropValue('title', u"My title")

断言门户根目录上属性的值

self.assertEqual(portal.getProperty('title'), u"My title")

portal_properties工具中的属性表中更改属性的值

from Products.CMFCore.utils import getToolByName

propertiesTool = getToolByName(portal, 'portal_properties')
siteProperties = propertiesTool['site_properties']

siteProperties._setPropValue('many_users', True)

断言在portal_properties工具中的属性表中的属性值

self.assertEqual(siteProperties.getProperty('many_users'), True)

安装产品和扩展配置文件

应用特定的扩展配置文件

from plone.app.testing import applyProfile

applyProfile(portal, 'my.product:default')

这是安装产品配置的首选方法。

使用附加组件控制面板将附加组件产品安装到Plone站点

from plone.app.testing import quickInstallProduct

quickInstallProduct(portal, 'my.product')

使用附加组件控制面板卸载和安装产品

quickInstallProduct(portal, 'my.product', reinstall=True)

请注意,这两个都假定产品的ZCML已加载,这通常在层设置期间完成。有关如何操作的更多详细信息,请参阅上面的层示例。

在编写具有安装扩展配置文件的产品时,通常需要编写在配置文件应用后检查站点状态的测试。以下是一些更常见的此类测试示例。

为了验证产品是否已安装(例如,通过 metadata.xml 作为依赖项)

from Products.CMFPlone.utils import get_installer

qi = get_installer(portal)
self.assertTrue(qi.is_product_installed('my.product'))

为了验证特定内容类型是否已安装(例如,通过 types.xml

typesTool = getToolByName(portal, 'portal_types')

self.assertNotEqual(typesTool.getTypeInfo('mytype'), None)

为了验证新的目录索引是否已安装(例如,通过 catalog.xml

catalog = getToolByName(portal, 'portal_catalog')

self.assertTrue('myindex' in catalog.indexes())

为了验证已添加新的目录元数据列(例如,通过 catalog.xml

self.assertTrue('myattr' in catalog.schema())

为了验证新工作流是否已安装(例如,通过 workflows.xml

workflowTool = getToolByName(portal, 'portal_workflow')

self.assertNotEqual(workflowTool.getWorkflowById('my_workflow'), None)

为了验证新工作流是否分配给类型(例如,通过 workflows.xml

self.assertEqual(dict(workflowTool.listChainOverrides())['mytype'], ('my_workflow',))

为了验证新工作流已被设置为默认(例如,通过 workflows.xml

self.assertEqual(workflowTool.getDefaultChain(), ('my_workflow',))

为了测试 portal_properties 工具中属性的值(例如,通过 propertiestool.xml 设置):

propertiesTool = getToolByName(portal, 'portal_properties')
siteProperties = propertiesTool['site_properties']

self.assertEqual(siteProperties.getProperty('some_property'), "some value")

为了验证在 portal_css 工具中已安装样式表(例如,通过 cssregistry.xml

cssRegistry = getToolByName(portal, 'portal_css')

self.assertTrue('mystyles.css' in cssRegistry.getResourceIds())

为了验证在 portal_javascripts 工具中已安装JavaScript资源(例如,通过 jsregistry.xml

jsRegistry = getToolByName(portal, 'portal_javascripts')

self.assertTrue('myscript.js' in jsRegistry.getResourceIds())

为了验证已添加新角色(例如,通过 rolemap.xml

self.assertTrue('NewRole' in portal.valid_roles())

为了验证已授予一组角色权限(例如,通过 rolemap.xml

roles = [r['name'] for r in portal.rolesOfPermission('My Permission') if r['selected']]
self.assertEqual(roles, ['Member', 'Manager'])

遍历

要遍历到视图、页面模板或其他资源,请使用 restrictedTraverse() 并提供一个相对路径

resource = portal.restrictedTraverse('f1/@@folder_contents')

返回值是一个视图对象、页面模板对象或其他资源。它可以被调用以获取实际响应(见下文)。

restrictedTraverse() 执行显式安全检查,因此如果当前测试用户没有查看给定资源的权限,可能会引发 Unauthorized。如果您不希望这样,您可以使用

resource = portal.unrestrictedTraverse('f1/@@folder_contents')

您还可以在文件夹或其他内容项上调用此操作,以从该起点遍历,例如,这相当于上面第一个示例

f1 = portal['f1']
resource = f1.restrictedTraverse('@@folder_contents')

请注意,此遍历不会考虑 IPublishTraverse 适配器,并且您不能传递查询字符串参数。实际上,restrictedTraverse()unrestrictedTraverse() 实现了与TAL中的路径表达式发生的遍历类型,这类似于,但并不相同于URL遍历。

要手动查找视图

from zope.component import getMultiAdapter

view = getMultiAdapter((f1, request), name=u"folder_contents")

请注意,这里的名称不应包含 @@ 前缀。

为了模拟 IPublishTraverse 适配器调用,假设视图实现了 IPublishTraverse

next = view.IPublishTraverse(request, u"some-name")

或者,如果 IPublishTraverse 适配器与视图分开

from zope.publisher.interfaces import IPublishTraverse

publishTraverse = getMultiAdapter((f1, request), IPublishTraverse)
next = view.IPublishTraverse(request, u"some-name")

为了模拟表单提交或查询字符串参数

request.form.update({
        'name': "John Smith",
        'age':  23
    })

form 字典包含序列化的请求。也就是说,如果您正在模拟使用如 :int(例如,上面的示例中的 age:int)这样的序列化器的查询字符串参数或提交的表单变量,那么在 form 字典中的值应该是序列化的(例如,上面的示例中是一个整数而不是字符串),并且名称应该是基本名称(age 而不是 age:int)。

要调用视图并获取响应正文作为字符串

view = f1.restrictedTraverse('@@folder_contents')
body = view()

self.assertFalse(u"An unexpected error occurred" in body)

请注意,这种方法并不完美。特别是,请求将不会有正确的URL或路径信息。如果您的视图依赖于这一点,您可以通过设置请求中的相关键来伪造它,例如

request.set('URL', f1.absolute_url() + '/@@folder_contents')
request.set('ACTUAL_URL', f1.absolute_url() + '/@@folder_contents')

要检查请求的状态(例如,在调用视图之后)

self.assertEqual(request.get('disable_border'), True)

要检查响应头(例如,在调用视图之后)

response = request.response

self.assertEqual(response.getHeader('content-type'), 'text/plain')

模拟浏览器交互

端到端功能测试可以使用 zope.testbrowser 模拟用户交互。这充当一个网络浏览器,通过特殊通道连接到 Zope,发送请求并获得响应。

注意: zope.testbrowser 完全在 Python 中运行,并不模拟 JavaScript 引擎。

请注意,要使用 zope.testbrowser,您需要使用功能测试层之一,例如 PLONE_FUNCTIONAL_TESTING,或者使用 FunctionalTesting 类实例化的另一个层。

如果您想创建一些初始内容,您可以在层中或在测试本身中这样做,在调用测试浏览器客户端之前。在后一种情况下,您需要在它可用之前提交事务,例如:

from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID

# Make some changes
setRoles(portal, TEST_USER_ID, ['Manager'])
portal.invokeFactory('Folder', 'f1', title=u"Folder 1")
setRoles(portal, TEST_USER_ID, ['Member'])

# Commit so that the test browser sees these changes
import transaction
transaction.commit()

要获取一个新的测试浏览器客户端

from plone.testing.z2 import Browser

# This is usually self.app (Zope root) or site.portal (test Plone site root)
browser = Browser(app)

要打开一个特定的 URL

portalURL = portal.absolute_url()
browser.open(portalURL)

要检查响应

self.assertTrue(u"Welcome" in browser.contents)

要检查响应头

self.assertEqual(browser.headers['content-type'], 'text/html; charset=utf-8')

要跟随链接

browser.getLink('Edit').click()

这通过文本获取链接。要通过 HTML id 获取链接

browser.getLink(id='edit-link').click()

要验证当前 URL

self.assertEqual(portalURL + '/edit', browser.url)

要设置表单控件值

browser.getControl('Age').value = u"30"

这通过关联的标签获取控件。要通过表单变量名称获取控件

browser.getControl(name='age:int').value = u"30"

请参阅 zope.testbrowser 文档,以获取有关如何选择和操作各种类型控件更多详细信息。

通过点击按钮提交表单

browser.getControl('Save').click()

同样,这使用标签来找到控件。要使用表单变量名称

browser.getControl(name='form.button.Save').click()

要模拟 HTTP BASIC 身份验证,并在所有请求中保持登录状态

from plone.app.testing import TEST_USER_NAME, TEST_USER_PASSWORD

browser.addHeader('Authorization', 'Basic %s:%s' % (TEST_USER_NAME, TEST_USER_PASSWORD,))

要模拟通过登录表单登录

browser.open(portalURL + '/login_form')
browser.getControl(name='__ac_name').value = TEST_USER_NAME
browser.getControl(name='__ac_password').value = TEST_USER_PASSWORD
browser.getControl(name='submit').click()

要模拟注销

browser.open(portalURL + '/logout')

调试技巧

默认情况下,当服务器发生错误时(例如,500 服务器端错误),仅显示 HTTP 错误代码。要查看更多详细信息,将 handleErrors 设置为 False

browser.handleErrors = False

要检查错误日志并获取最新条目的完整跟踪信息

from Products.CMFCore.utils import getToolByName

errorLog = getToolByName(portal, 'error_log')
print errorLog.getLogEntries()[-1]['tb_text']

要将当前响应保存到 HTML 文件

open('/tmp/testbrowser.html', 'w').write(browser.contents)

您现在可以打开此文件,并使用 Firebug 等工具检查页面结构。之后应删除该文件。

与 ZopeTestCase/PloneTestCase 的比较

plone.testingplone.app.testing 部分是从 ZopeTestCase 发展而来的,它包含在 Zope 2 的 Testing 包中,以及与 Plone 一起提供的 Products.PloneTestCase,该测试用例被 Plone 本身以及许多附加产品使用。

如果您熟悉 ZopeTestCasePloneTestCase,这些包的概念应该对您来说是熟悉的。但是,有一些重要的区别需要记住。

  • plone.testingplone.app.testing 没有像 ZopeTestCasePloneTestCase 那样背负着遗产支持。这使得它们更小、更容易理解和维护。

  • 相反,plone.testing 仅适用于 Python 2.6 和 Zope 2.12 及更高版本。 plone.app.testing 仅适用于 Plone 4 及更高版本。如果您需要编写针对旧版 Plone 运行的测试,您将需要使用 PloneTestCase

  • ZopeTestCase/PloneTestCase 是在层可用作为设置机制之前编写的。 plone.testing 非常注重层。

  • PloneTestCase 提供了一个基类,也称为 PloneTestCase,您必须使用它,因为它负责设置和清理。 plone.testing 将共享状态移动到层和层资源,并且不对测试强加任何特定的基类。这有时意味着需要多输入一些内容(例如,self.layer['portal']self.portal),但它使得控制和使用测试固定装置变得更加容易。这也有助于使您的测试代码更简单、更明确。

  • ZopeTestCase 有一个 installProduct() 函数和一个相应的 installPackage() 函数。 plone.testing 只有一个 installProduct(),它可以配置任何类型的 Zope 2 产品(即 Products.* 命名空间中的包,特殊 Products 文件夹中的旧式产品,或任何命名空间中的包,这些包已加载其 ZCML 并在其配置中包含 <five:registerPackage /> 指令)。请注意,即使对于 Products.* 命名空间中的“旧式”产品,您也必须传递一个完整的点分名称,例如 Products.LinguaPlone 而不是 LinguaPlone

  • 在设置过程中,PloneTestCase 将加载 Zope 2 的默认 site.zcml。这反过来又会加载 Products.* 命名空间中所有包的 ZCML。 plone.testing 不这样做(并且强烈建议您自己这样做),因为这很容易意外地将您不希望存在的包包含在您的固定装置中(并且实际上可能会大大改变固定装置)。您应显式加载您包的 ZCML。有关详细信息,请参阅 plone.testing 文档。

  • 当使用 PloneTestCase 时,任何加载到 sys.path 并定义了 z3c.autoinclude.plugin:plone 入口点的包将通过 z3c.autoinclude 的插件机制加载。这种加载被明确禁用,原因与 Products.* 自动加载相同。您应显式加载您包的配置。

  • PloneTestCase 设置了一个基本固定装置,其中启用了成员文件夹,并且测试用户的成员文件夹作为 self.folder 可用。 plone_workflow 工作流也作为默认安装。 plone.app.testing 采用更简约的方法。要创建一个由测试用户拥有的测试文件夹,类似于 PloneTestCase 中的 self.folder,您可以这样做

    import unittest2 as unittest
    from plone.app.testing import TEST_USER_ID, setRoles
    from plone.app.testing import PLONE_INTEGRATION_TESTING
    
    class MyTest(unitest.TestCase):
    
        layer = PLONE_INTEGRATION_TESTING
    
        def setUp(self):
            self.portal = self.layer['portal']
    
            setRoles(self.portal, TEST_USER_ID, ['Manager'])
            self.portal.invokeFactory('Folder', 'test-folder')
            setRoles(self.portal, TEST_USER_ID, ['Member'])
    
            self.folder = self.portal['test-folder']

    当然,您也可以在自己的层中执行此类设置,并将其作为资源公开。

  • 要使用 zope.testbrowserPloneTestCase,您应使用其 FunctionalTestCase 作为基类,然后使用以下模式

    from Products.Five.testbrowser import Browser
    browser = Browser()

    plone.app.testing 中的等效模式是使用 FunctionalTesting 测试生命周期层(请参阅上面的示例),然后使用

    from plone.testing.z2 import Browser
    browser = Browser(self.layer['app'])

    请注意,如果您在调用 browser.open() 之前对固定装置进行了更改,则它们将 不会 显示,直到您执行显式提交。有关详细信息,请参阅上面的 zope.testbrowser 示例。

变更日志

7.1.0 (2024-07-31)

新功能

  • PloneFixture:显式安装 plone.app.contenttypes:default。Plone 6.1 中的 addPloneSite 工厂不再默认安装此内容。[maurits] (#3961)

错误修复

  • 删除 setuptools 遗留物。[maurits] (#72)

7.0.2 (2024-01-19)

内部

  • 更新配置文件。[plone 开发者] (cfffba8c)

7.0.1 (2023-04-15)

内部

  • 更新配置文件。[plone 开发者] (434550cc)

7.0.0 (2022-12-02)

错误修复

  • Plone 6.0.0 的最终发布版。(#600)

7.0.0b2 (2022-10-11)

错误修复

  • 恢复之前使用的管理员密码(“secret”)以进行测试。[davisagli] (#79)

7.0.0b1 (2022-09-30)

错误修复

  • 增加测试密码长度。[davisagli] (#78)

7.0.0a3 (2022-04-28)

新功能

  • 在 Plone 5 中加载 CMFFormController,当在 Plone 6 中失败时保持沉默。这暂时恢复了与 Plone 5.2 的兼容性。注意,此 plone.app.testing 版本仅适用于 Python 3。[maurits] (#3467)

7.0.0a2 (2022-04-04)

错误修复

  • 在 PloneFixture 中创建门户后添加保存点。修复了 问题 3467。[pbauer] (#76)

7.0.0a1 (2022-03-23)

重大更改

  • 仅适用于 Plone 6。[#75]

新功能

  • 移除 CMFFormController [petschki] (#75)

6.1.9 (2021-09-01)

错误修复

  • 修复了导致 dexterity 站点根失败测试的问题。[jaroel, ale-rt] (#60)

6.1.8 (2020-11-11)

错误修复

  • 在尝试加载 plone.app.folder 的 zcml 之前,请确保它是一个真实的包或由 plone.app.upgrade 提供的别名。[#72]

6.1.7 (2020-10-12)

新功能

  • 删除了旧快速安装程序的向后兼容代码。当前的 plone.app.testing 仅适用于 Plone 5.2+,因此此代码不再使用。有关更多信息,请参阅 PLIP 1775。[maurits] (#1775)

6.1.6 (2020-09-26)

错误修复

  • 修复了在 Python 3 上使用 Products.MailHost 4.10 时失败的测试。[maurits] (#3178)

6.1.5 (2020-04-20)

错误修复

  • 较小的打包更新。[#1]

6.1.4 (2020-03-09)

错误修复

  • 修复了一个测试隔离问题,该问题阻止了 MOCK_MAILHOST_FIXTURE 在多个测试用例中使用。[ale-rt] (#61)

  • MockMailHostLayer 配置了邮件发送者,设置适当的注册记录(修复了 #62)。[#62]

  • 修复了使用 zope.testrunner 内部版本 5.1 时失败的测试。[jensens] (#68)

  • 不要加载不再存在的 Products.ResourceRegistries 的 Products/ZCML。[jensens] (#69)

6.1.3 (2019-02-16)

错误修复

  • 在 py2 中将 plone.app.folder 作为一个可选产品添加到 PloneFixture。[#59]

6.1.2 (2019-02-13)

错误修复

  • 修复了 travis 构建检查 Plone 实际支持的 Python 版本的问题。还修复了 setup.py 中的 Python 版本。[#57]

6.1.1 (2018-11-05)

错误修复

  • 修复了未包含某些文件的包清单。[ale-rt]

6.1.0 (2018-11-04)

重大更改

  • 需要 plone.testing >= 7.0

新功能

  • 添加对 Python 3.5 和 3.6 的支持。[loechel, ale-rt, icemac, davisagli, pbauer]

6.0.0 (2018-10-05)

新功能

  • 仅在可导入时安装和加载 CMFQuickInstallerTool 的 zcml。[maurits]

  • 从 plone.i18n 加载 negotiator(已删除 PTS)。[jensens, ksuess]

  • 添加 bbb.PloneTestCase 的副本。对于 Plone 5.2,bbb.PloneTestCase 将使用 Dexterity 而不是 Archetypes。添加 bbb_at.PloneTestCase 以供它们使用,这样就可以保持 AT 测试的正常工作。请参阅 https://github.com/plone/plone.app.testing/pull/51 [pbauer]

错误修复

  • 修改 doctests 以与 plone.testing 自动层端口选择兼容。[Rotonen]

5.0.8 (2017-10-25)

错误修复

  • 加载 Products.PageTemplates ZCML。[tschorr]

5.0.7 (2017-07-03)

错误修复

  • 从 BrowserViews 中移除已弃用的 __of__ 调用。[MrTango]

  • 移除 unittest2 依赖。[kakshay21]

5.0.6 (2016-12-19)

错误修复

  • 不再尝试加载 Products.SecureMailHost 及其 zcml。这不是 Plone 5.0 或更高版本的一部分。[maurits]

5.0.5 (2016-11-19)

错误修复

  • 如果不可用,则在 PloneFixture 中不要使用 install Products.PasswordResetTool。[thet]

5.0.4 (2016-09-23)

新功能

  • 如果可用,请使用 get_installer 而不是 portal_quickinstaller,对于 Plone 5.1 及更高版本。[maurits]

  • 在 PloneSandboxLayer 中使配置文件升级版本持久。这样,在 teardown 中将重置安装的配置文件版本。[maurits]

5.0.3 (2016-09-07)

错误修复

  • 在测试中加载 Products.CMFFormController。它仍然由核心 Plone 使用,即使在没有 Archetypes 的情况下也是如此。这使得 CMFFormController 测试通过。[maurits]

5.0.2 (2016-06-07)

修复

  • 如果不可用,则不要在 PloneFixture 中安装 Products.SecureMailHost。[vangheem]

5.0.1 (2016-02-26)

修复

  • zope.component.hooks 替换已弃用的 zope.site.hooks 导入。[thet]

5.0.0 (2016-02-20)

新功能

  • 添加一个 MOCK_MAILHOST_FIXTURE 固定件,集成和功能测试层可以依赖于它。这允许轻松检查邮件如何从 Plone 发送。[gforcada]

修复

  • 修复 layers.rst doctest 以与旧版和新版 zope.testrunner 层排序兼容。[thet]

  • 依赖于 zope.testrunner 并修复 zope.testing.testrunner 的弃用用法。[thet]

  • 清理代码,flake8,排序导入等。[gforcada]

  • 使用bbb.PloneTestCase修复RAM缓存错误。[ebrehault]

5.0b6 (2015-08-22)

  • 不需要unittest2。[gforcada]

5.0b5 (2015-07-18)

  • 不要安装CMFDefault。[tomgross]

  • 记录PloneWithPackageLayer。[gotcha]

5.0b4 (2015-05-04)

  • 不要安装CMFFormController。[timo]

  • 不要安装CMFDefault。[tomgross]

5.0b3 (2015-03-26)

  • 从PloneFixture中移除PloneLanguageTool。[timo]

5.0b2 (2015-03-13)

  • 移除应用扩展配置文件的测试,因为我们目前没有好的测试用例。[davidagli]

  • 修复测试,plone.app.theming没有记录为已安装。[davisagli]

  • 修复:Products.CMFPlone需要来自plone.app.foldergopip索引。因此,必须在应用CMFPlones配置文件(将索引安装到目录中)之前初始化后者的配置。目前,CMFPlone因此自行注册索引,但plone.app.folder也注册了它,这导致了plone/Products.CMFPlone#313“GopipIndex注册两次”错误。在测试中,由于plone.app.folder从未被初始化为z2产品,注册失败。为了从CMFPlone中删除误导性注册,我们必须确保索引可用,这是通过此更改实现的。此外,对受影响的文件进行了微小的PEP8优化。[jensens]

  • 如果测试中不存在,则创建memberfolder。[tomgross]

5.0b1 (2014-10-23)

  • 允许applyProfile跳过步骤以及portal_setup-tool支持的其它所有选项。[pbauer, tomgross]

5.0a2 (2014-04-19)

  • 为PLONE_FIXTURE层安装Products.DateRecurringIndex。[thet]

5.0a1 (2014-02-22)

  • 将‘ROBOT_TEST_LEVEL’添加到接口中,以便其他包可以导入它。这使我们更容易决定更改值。[timo]

  • 替换已弃用的测试断言语句。[timo]

  • plonetheme.classic不再随Plone一起提供,不要用于测试。[esteele]

  • 如果在PloneSandboxLayer setUp期间发生错误,则清理zodbDB和configurationContext资源。[davisagli]

  • 让PLONE_FIXTURE不安装内容类型系统。需要内容类型来运行测试的包应从plone.app.contenttypes或Products.ATContentTypes中选择适当的测试用例。[davisagli]

  • 将[robot]额外依赖项锁定为robotsuite>=1.4.0。[saily]

  • 修复quickInstallProduct中reinstallProducts方法拼写错误。[saily]

  • 将bbb PloneTestCase类与原始类同步。[tomgross]

4.2.2 (2013-02-09)

  • 为使用Selenium2Library进行Robot Framework测试添加[robot]额外依赖项。[datakurre]

  • 将PythonScripts作为zope产品安装。[mikejmets]

4.2.1 (2012-12-15)

  • 允许使用非标准端口进行测试。允许并行运行多个测试套件。[do3cc]

  • 更新文档。[moo]

4.2 (2012-04-15)

  • 由于plone.app.collection的添加破坏了向后兼容性,分支为4.2。[esteele]

  • 通过使用更长的超时,修复了我们自己的测试中的虚假失败。[maurits]

  • 将plone.app.collection添加到PloneFixture。[timo]

4.0.2 (2011-08-31)

  • 中安装Zope产品之前加载ZCML;它启用了包注册。[gotcha]

4.0.1 (2011-07-14)

  • 参数添加到辅助类,以安装额外的Zope 2产品。[jfroche]

4.0 - 2011-05-13

  • 4.0 最终发布。[esteele]

  • 添加MANIFEST.in。[WouterVH]

4.0a6 - 2011-04-06

  • 为selenium层添加辅助函数。(从Products.CMFPlone/Products/CMFPlone/tests/selenium/base.py中的SeleniumTestCase复制。)[emanlove]

  • 重新设计SeleniumLayer的层设置,使z2.ZSERVER_FIXTURE成为默认_base。[esteele]

  • 在模块查找之前将传递的selenium webdriver名称转换为小写。[esteele]

  • 将selenium启动和关闭移动到testSetUp和testTearDown,分别。这样做是为了进一步隔离单个测试。例如,在某个测试中登录需要注销或关闭浏览器,现在selenium_layer将在testTearDown中这样做,以便在下一个测试中有“干净”的状态。[emanlove]

  • 纠正使用selenium 2.0b2的各个selenium webdriver的模块路径。[emanlove]

4.0a5 - 2011-03-02

  • 使用新的 plone.testing.security 模块确保在基于 PloneSandboxLayer 辅助基类设置和删除层时,安全检查器的隔离。这在同一测试运行中运行多个测试套件时会导致问题,尤其是如果其中一个套件正在设置使用 five.grok 的 ZCML。 [optilude]

4.0a4 - 2011-01-11

  • 使用 PloneSandboxLayer 时,通过快照自动删除 PAS 注册。考虑到插件可能通过传递依赖关系在 ZCML 中注册,手动执行此操作过于困难。无需担心向后兼容性 - 仍然支持使用 tearDownMultiPlugin(),并且通常只需调用一次。 [optilude]

  • 确保 tearDownMultiPlugin() 和通用的 PAS 插件清理处理程序不会干扰 PAS ZCML 指令的清理处理程序。[optilude]

  • 不要安装 Products.kupuProducts.CMFPlacefulWorkflow。 [elro]

  • 依赖 Products.CMFPlone 而不是 Plone。 [elro]

4.0a3 - 2010-12-14

  • 允许导入 PloneTestLifecycle 的顶级。 [stefan]

  • 添加了警告,不要使用“默认”Firefox配置文件进行 selenium 测试。 [zupo]

  • 修复了分布依赖声明。 [hannosch]

  • 将许可协议更正为仅适用于 GPL 版本 2。 [hannosch]

  • 根据 optilude 的建议,在已经政策繁重的辅助类上添加一些模块导入辅助方法。 [rossp]

  • 添加一个层和测试用例来运行 selenium 测试。 [rossp]

  • 确保默认测试用户的用户 ID 和登录名不同。这有助于揭示用户 ID 与登录名错误的问题,这是一个非常常见的错误。[wichert]

1.0a2 - 2010-09-05

  • 确保在层设置期间正确安装 plone.app.imaging。 [optilude]

1.0a1 - 2010-08-01

  • 初始发布

项目详情


发布历史 发布通知 | RSS 源

下载文件

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

源分发

plone_app_testing-7.1.0.tar.gz (110.9 kB 查看哈希值)

上传时间

构建分发

plone.app.testing-7.1.0-py3-none-any.whl (49.5 kB 查看哈希值)

上传时间 Python 3

由以下支持

AWSAWS 云计算和安全赞助商 DatadogDatadog 监控 FastlyFastly CDN GoogleGoogle 下载分析 MicrosoftMicrosoft PSF赞助商 PingdomPingdom 监控 SentrySentry 错误日志 StatusPageStatusPage 状态页面