{ "info": { "author": "Ross Patterson", "author_email": "me@rpatterson.net", "bugtrack_url": null, "classifiers": [ "Programming Language :: Python", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": ".. -*-doctest-*-\n\n========================\ncollective.testcaselayer\n========================\n\nThe support for layers provided by zope.testing helps to lessen the\namount of time consumed during test driven development by sharing\nexpensive test fixtures, such as is often requires for functional\ntest. This package provides several well tested facilities to make\nwriting and using layers faster and easier.\n\nThe collective.testcaselayer.common.common_layer, used in the Quick\nStart, also includes some commonly useful test fixtures:\n\n - a mock mail host\n - remove 'Unauthorized' and 'NotFound' from error_log ignored\n exceptions\n - puts the resources registries in debug mode\n (portal_css, portal_javascripts, portal_kss)\n\n.. -*-doctest-*-\n\nQuick Start\n===========\n\nFor a simple testing layer which installs a collective namespace\npackage into Zope and installs it's GenericSetup profile into the\nPloneTestCase Plone site you can do the following.\n\nSpecify the testing dependency on collective.testcaselayer in the\negg's setup.py::\n\n from setuptools import setup, find_packages\n ...\n tests_require = ['collective.testcaselayer']\n ...\n setup(name='collective.foo',\n ...\n install_requires=[\n 'setuptools',\n # -*- Extra requirements: -*-\n ],\n tests_require=tests_require,\n extras_require={'tests': tests_require},\n ...\n entry_points=\"\"\"\n\nTell your buildout to include the testing dependencies. This is only\nnecessary for deployments where you'll be running the tests. As such,\nyou can leave this out of your production buildout configuration and\nput it only in your buildout's development configuration::\n\n ...\n eggs +=\n collective.foo [tests]\n ...\n\nDefine the layer. The layer can use all the same methods as a\nPloneTestCase class, such as:\n\n - self.login(user_name)\n - self.loginAsPortalOwner()\n - self.addProduct(product)\n - self.addProfile(profile)\n\nAn additional, method is provided to load a ZCML file with ZCML debug\nmode enabled:\n\n - self.loadZCML(file, package=package)\n\nYou could use a collective.foo.testing module like this:\n\n >>> from Products.PloneTestCase import ptc\n >>>\n >>> from collective.testcaselayer import ptc as tcl_ptc\n >>> from collective.testcaselayer import common\n >>>\n >>> class Layer(tcl_ptc.BasePTCLayer):\n ... \"\"\"Install collective.foo\"\"\"\n ...\n ... def afterSetUp(self):\n ... ZopeTestCase.installPackage('collective.foo')\n ...\n ... from collective.foo import tests\n ... self.loadZCML('testing.zcml', package=tests)\n ...\n ... self.addProfile('collective.foo:default')\n >>>\n >>> layer = Layer([common.common_layer])\n\nTo use this layer in a README.txt doctest, you could use a\ncollective.foo.tests module like this:\n\n >>> import unittest\n >>> import doctest\n >>>\n >>> from Testing import ZopeTestCase\n >>> from Products.PloneTestCase import ptc\n >>>\n >>> from collective.foo import testing\n >>>\n >>> optionflags = (doctest.NORMALIZE_WHITESPACE|\n ... doctest.ELLIPSIS|\n ... doctest.REPORT_NDIFF)\n >>>\n >>> def test_suite():\n ... suite = ZopeTestCase.FunctionalDocFileSuite(\n ... 'README.txt',\n ... optionflags=optionflags,\n ... test_class=ptc.FunctionalTestCase)\n ... suite.layer = testing.layer\n ... return suite\n >>>\n >>> if __name__ == '__main__':\n ... unittest.main(defaultTest='test_suite')\n\nNow write your README.txt doctest and your tests can be run with\nsomething like::\n\n $ bin/instance test -s collective.foo\n\nDetailed Documentation\n======================\n\n.. contents:: Table of Contents\n\nLayer authors often end up reproducing the functionality provided by\ntheir test case classes since the same functionality is needed to\nperform layer set up or tear down. The collective.testcaselayer.ztc,\ncollective.testcaselayer.ctc, and collective.testcaselayer.ptc modules\nprovide layer base classes that mix in the test case functionality from\nZopeTestCase, CMFTestCase, and PloneTestCase, respectively. See the\ncollective.testcaselayer.ztc, and collective.testcaselayer.ptc\nsections below (or ztc.txt and ptc.txt if reading this in the source)\nfor more details. These layer base classes also include the layer\nbase class support from collective.testcaselayer.layer and the\nsandboxed ZODB layer support from collective.testcaselayer.sandbox\ndescribed below. Additionally, these modules allow for using the test\ncase fixtures as layers themselves.\n\nWhile class objects can be used as layers, as opposed to instances of\nclasses, doing so means that it is not possible for a layer to\nsubclass another layer *just* to re-use functionality without also\ndepending on that layer being set up as well. See the\ncollective.testcaselayer.layer section below (or layer.txt if reading\nthis in the source) for more details.\n\nThe DemoStorage included with the ZODB provides a way to \"nest\" ZODB\nstores such that all writes will go to the DemoStorage while reads\nwill be taken from the base storage if not available from the\nDemoStorage. The collective.testcaselayer.sandbox module uses this\nfeature to associate a DemoStorage with each sandboxed layer to which\nset up changes are committed and restore the base storage on tear\ndown. Thus sibling layers that write to the ZODB can be isolated from\neach other. See the collective.testcaselayer.sandbox section below\n(or sandbox.txt if reading this in the source) for more details.\n\n.. -*-doctest-*-\n\nCommon Layer\n============\n\nIf a testing layer uses the\ncollective.testcaselayer.common.common_layer as a base layer then a\nfew commonly useful things will be set up.\n\nBefore setting up the layer, the default exceptions are ignored in the\nerror_log and the resource registries are not in debug mode.\n\n >>> portal.error_log.getProperties()['ignored_exceptions']\n ('Unauthorized', 'NotFound', 'Redirect')\n\n >>> portal.portal_css.getDebugMode()\n False\n >>> portal.portal_javascripts.getDebugMode()\n False\n >>> 'portal_kss' in portal and portal.portal_kss.getDebugMode() or False\n False\n\nSet up the common_layer.\n\n >>> from zope.testing.testrunner import runner\n >>> from collective.testcaselayer import common\n\n >>> def getSetUpLayers(layer):\n ... for base in layer.__bases__:\n ... if base is not object:\n ... for recurs in getSetUpLayers(base):\n ... yield recurs\n ... yield base\n >>> setup_layers = dict((layer, 1) for layer in\n ... getSetUpLayers(common.common_layer))\n\n >>> options = runner.get_options([], [])\n >>> runner.setup_layer(options, common.common_layer, setup_layers)\n Set up collective.testcaselayer.common.CommonPTCLayer in ... seconds.\n\nNow only 'Redirect' is ignored in error_log, and the resources\nregistries are in debug mode.\n\n >>> from Testing import ZopeTestCase\n >>> from Products.PloneTestCase import ptc as plone_ptc\n >>> app = ZopeTestCase.app()\n >>> portal = getattr(app, plone_ptc.portal_name)\n\n >>> portal.error_log.getProperties()['ignored_exceptions']\n ('Redirect',)\n\n >>> portal.portal_css.getDebugMode()\n True\n >>> portal.portal_javascripts.getDebugMode()\n True\n >>> 'portal_kss' in portal and portal.portal_kss.getDebugMode() or True\n True\n\n.. -*-doctest-*-\n\nMock Mailhost\n=============\n\nIf a testing layer uses the\ncollective.testcaselayer.mail.mockmailhost_layer as a base layer then\nmessages sent with the portal.MailHost.send method will be appended to\na list for checking in tests.\n\nStart with an empty MailHost.\n\n >>> len(portal.MailHost)\n 0\n\nSend a message.\n\n >>> portal.MailHost.send(\"\"\"\\\n ... From: foo@foo.com\n ... To: bar@foo.com\n ... Subject: Foo message subject\n ...\n ... Foo message body\n ... \"\"\")\n\nThe MailHost now contains one message.\n\n >>> len(portal.MailHost)\n 1\n\nThe message an be removed using the pop method in which case it's\nremoved from the list.\n\n >>> print portal.MailHost.pop().as_string()\n From: foo@foo.com\n To: bar@foo.com\n Subject: Foo message subject\n Date: ...\n Foo message body\n >>> len(portal.MailHost)\n 0\n\nThe mock mail host can handle more complicated call signatures used in\nthe wild.\n\n >>> portal.MailHost.send(\n ... \"\"\"\\\n ... From: foo@foo.com\n ... To: bar@foo.com\n ... Subject: Qux message subject\n ...\n ... Qux message body\n ... \"\"\", 'bar@foo.com', 'foo@foo.com',\n ... subject='Qux message subject')\n\n >>> print portal.MailHost.pop().as_string()\n To: bar@foo.com...\n Qux message body\n >>> len(portal.MailHost)\n 0\n\n.. -*-doctest-*-\n\ncollective.testcaselayer.ptc\n============================\n\nThe collective.testcaselayer.ptc module extends the layers and layer\nbase classes from collective.testcaselayer.ztc to PloneTestCase. See\nztc.txt for an introduction to using the layers and layer base\nclasses. Here we will only demonstrate that the facilities specific\nto PloneTestCase not inherited from ZopeTestCase.\n\nLayers\n------\n\nThe PloneTestCase test fixture can be set up and torn down as a layer.\n\n >>> from collective.testcaselayer import ptc\n >>> ptc.ptc_layer\n \n\nTo test the effects of just this layer, set up the base layer\nseparately. Because of the way PloneTestCase uses layers, we must first\ncall the setupPloneSite() function.\n\n >>> from zope.testing.testrunner import runner\n >>> from Products.PloneTestCase import ptc as plone_ptc\n >>> plonesite_layer, = ptc.ptc_layer.__bases__\n >>> options = runner.get_options([], [])\n >>> setup_layers = {}\n >>> runner.setup_layer(options, plonesite_layer, setup_layers)\n Set up...Products.PloneTestCase.layer.ZCML in ... seconds.\n Set up Products.PloneTestCase.layer.PloneSite in ... seconds.\n\nThe PloneTestCase test fixture has not been set up.\n\n >>> from Testing import ZopeTestCase\n >>> app = ZopeTestCase.app()\n >>> portal = getattr(app, plone_ptc.portal_name)\n >>> portal.acl_users.getUserById(plone_ptc.default_user)\n >>> ZopeTestCase.close(app)\n\nSet up the PloneTestCase layer.\n\n >>> runner.setup_layer(options, ptc.ptc_layer, setup_layers)\n Set up collective.testcaselayer.ptc.PTCLayer in ... seconds.\n\nThe PloneTestCase test fixture has been set up.\n\n >>> app = ZopeTestCase.app()\n >>> portal = getattr(app, plone_ptc.portal_name)\n >>> portal.acl_users.getUserById(plone_ptc.default_user)\n \n >>> ZopeTestCase.close(app)\n\nTear down the PloneTestCase layer.\n\n >>> runner.tear_down_unneeded(\n ... options,\n ... [layer for layer in setup_layers\n ... if layer is not ptc.ptc_layer],\n ... setup_layers)\n Tear down collective.testcaselayer.ptc.PTCLayer in ... seconds.\n\nThe PloneTestCase test fixture is no longer set up.\n\n >>> app = ZopeTestCase.app()\n >>> portal = getattr(app, plone_ptc.portal_name)\n >>> portal.acl_users.getUserById(plone_ptc.default_user)\n >>> ZopeTestCase.close(app)\n\nLayer Base Classes\n------------------\n\nThe PloneTestCase class facilities can also be used in layers that use\nthe PloneTestCase layer base class.\n\n >>> class FooLayer(ptc.BasePTCLayer):\n ... def afterSetUp(self):\n ... self.addProfile(\n ... 'Products.CMFDefault:sample_content')\n ... self.addProduct('CollectiveTestCaseLayerTesting')\n ... self.loginAsPortalOwner()\n\nThis layer depends on the profile and product added which are set up\nin a testing only layer.\n\n >>> from collective.testcaselayer.testing import layer\n >>> foo_layer = FooLayer([layer.product_layer, ptc.ptc_layer])\n\nThe FooLayer test fixture has not been set up.\n\n >>> app = ZopeTestCase.app()\n >>> portal = getattr(app, plone_ptc.portal_name)\n\n >>> hasattr(portal, 'subfolder')\n False\n >>> hasattr(portal, 'foo')\n False\n\n >>> from AccessControl import SecurityManagement\n >>> SecurityManagement.getSecurityManager().getUser()\n \n\n >>> ZopeTestCase.close(app)\n\nSet up the FooLayer.\n\n >>> runner.setup_layer(options, foo_layer, setup_layers)\n Set up collective.testcaselayer.testing.layer.ProductLayer\n in ... seconds.\n Set up FooLayer in ... seconds.\n\nThe FooLayer test fixture has been set up.\n\n >>> app = ZopeTestCase.app()\n >>> portal = getattr(app, plone_ptc.portal_name)\n\n >>> portal.subfolder\n \n >>> portal.foo\n 'foo'\n\n >>> from AccessControl import SecurityManagement\n >>> SecurityManagement.getSecurityManager().getUser()\n \n\n >>> ZopeTestCase.close(app)\n\nTear down the FooLayer.\n\n >>> runner.tear_down_unneeded(\n ... options,\n ... [layer for layer in setup_layers\n ... if layer is not foo_layer],\n ... setup_layers)\n Tear down FooLayer in ... seconds.\n\nThe FooLayer test fixture is no longer set up.\n\n >>> app = ZopeTestCase.app()\n >>> portal = getattr(app, plone_ptc.portal_name)\n\n >>> hasattr(portal, 'subfolder')\n False\n >>> hasattr(portal, 'foo')\n False\n\n >>> from AccessControl import SecurityManagement\n >>> SecurityManagement.getSecurityManager().getUser()\n \n\n >>> ZopeTestCase.close(app)\n\nFinish tearing down the rest of the layers.\n\n >>> runner.tear_down_unneeded(options, [], setup_layers)\n Tear down collective.testcaselayer.testing.layer.ProductLayer\n in ... seconds.\n Tear down Products.PloneTestCase.layer.PloneSite in ... seconds.\n Tear down Products.PloneTestCase.layer.ZCML in ... seconds.\n\n.. -*-doctest-*-\n\ncollective.testcaselayer.ztc\n============================\n\nThe BaseZTCLayer and cousins are intended to be used as base classes\nfor layers to allow them to use the facilities of ZopeTestCase,\nPortalTestCase, and their subclasses. Thus, the layer setUp and\ntearDown methods can use the test case methods and other support such\nas: self.login(), self.logout(), self.loginAsPortalOwner(),\nself.setRoles(), self.setPermissions(), etc..\n\nThe ZTCLayer and cousins allow using the test fixture setup by any of\nthe test cases as a layer itself.\n\nThe collective.testcaselayer.ctc and collective.testcaselayer.ptc\nmodules extend this support to CMFTestCase and PloneTestCase, though\ncollective.testcaselayer does not depend on them itself. These layer\nbase classes allow for use of those test cases' methods such as\naddProfile() and addProduct() see ctc.txt and ptc.txt for more\ndetails.\n\nLayers\n------\n\nThe collective.testcaselayer.ztc module provides sandboxed layers that\nset up the test fixtures for ZopeTestCase. Note that test case based\nlayers still act like test cases with a special no-op layerOnly() test\nmethod to that they have functional str() and repr() values.\n\n >>> from collective.testcaselayer import ztc\n >>> ztc.ztc_layer\n \n\nBefore we set up ZopeTestCase as a layer, nothing has been set up.\n\n >>> from AccessControl import SecurityManagement\n >>> SecurityManagement.getSecurityManager().getUser()\n \n\n >>> hasattr(ztc.ztc_layer, 'app')\n False\n\n >>> from Testing import ZopeTestCase\n >>> app = ZopeTestCase.app()\n >>> 'test_folder_1_' in app.objectIds()\n False\n >>> ZopeTestCase.close(app)\n\n >>> from Testing.ZopeTestCase import connections\n >>> connections.count()\n 0\n\nSet up ZopeTestCase as a layer.\n\n >>> from zope.testing.testrunner import runner\n >>> options = runner.get_options([], [])\n >>> setup_layers = {}\n >>> runner.setup_layer(options, ztc.ztc_layer, setup_layers)\n Set up collective.testcaselayer.ztc.ZTCLayer in ... seconds.\n\nThe ZopeTestCase test fixture has been set up, but there is no logged\nin user.\n\n >>> SecurityManagement.getSecurityManager().getUser()\n \n\n >>> 'test_folder_1_' in ztc.ztc_layer.app.objectIds()\n True\n\nAlso note that the app attribute of the layer represents an open\nconnection to the ZODB.\n\n >>> connections.count()\n 1\n\nTear down the ZopeTestCase layer.\n\n >>> runner.tear_down_unneeded(options, [], setup_layers)\n Tear down collective.testcaselayer.ztc.ZTCLayer in ... seconds.\n\nNow everything is back to its previous state.\n\n >>> SecurityManagement.getSecurityManager().getUser()\n \n\n >>> hasattr(ztc.ztc_layer, 'app')\n False\n\n >>> from Testing import ZopeTestCase\n >>> app = ZopeTestCase.app()\n >>> 'test_folder_1_' in app.objectIds()\n False\n >>> ZopeTestCase.close(app)\n\n >>> connections.count()\n 0\n\nLayer Base Classes\n------------------\n\nThe collective.testcaselayer.ztc module also provides base classes for\nsandboxed layers that don't actually set up the test case fixtures but\nallow using the facilities provided by the test cases in the layer set\nup and tear down code.\n\nSince layers can be nested, these layer base classes don't do the\nactual ZopeTestCase test fixture set up unless a subclass explicitly\nsets _setup_fixture (or _configure_portal for PortalTestCase) to True.\nBest practice should be to instantiate any layers depending on the ZTC\ntest fixture with the ZTCLayer as a base layer as above.\n\nCreate a layer class that subclasses the appropriate base layer class.\nThis layer class overrides the afterSetUp() method just as with\nZopeTestCase based test cases. The afterSetUp method here excercises\nthe factilities provided by ZopeTestCase and an additional loadZCML()\nmethod for loading ZCML files with ZCML debug mode enabled.\n\n >>> from collective.testcaselayer import testing\n >>> class FooLayer(ztc.BaseZTCLayer):\n ... def afterSetUp(self):\n ... self.login()\n ... self.setRoles(['Manager'])\n ... self.loadZCML('loadzcml.zcml', package=testing)\n >>> foo_layer = FooLayer([ztc.ztc_layer])\n\nTo test the effects of just this layer, set up the base layer\nseparately.\n\n >>> runner.setup_layer(options, ztc.ztc_layer, setup_layers)\n Set up collective.testcaselayer.ztc.ZTCLayer in ... seconds.\n\nBefore setting up the new layer, only the ZopeTestCase fixture is set\nup.\n\n >>> SecurityManagement.getSecurityManager().getUser()\n \n\n >>> app = ZopeTestCase.app()\n >>> user = getattr(app, ZopeTestCase.folder_name\n ... ).acl_users.getUserById(ZopeTestCase.user_name)\n >>> user.getRoles()\n ('test_role_1_', 'Authenticated')\n >>> ZopeTestCase.close(app)\n\nSet up the new layer.\n\n >>> runner.setup_layer(options, foo_layer, setup_layers)\n Set up FooLayer in ... seconds.\n\nNow the changed made by afterSetUp() are reflected.\n\n >>> authenticated = SecurityManagement.getSecurityManager(\n ... ).getUser()\n >>> authenticated\n \n >>> authenticated.getRoles()\n ('Manager', 'Authenticated')\n\nTear down just the new layer.\n\n >>> runner.tear_down_unneeded(\n ... options, [ztc.ztc_layer], setup_layers)\n Tear down FooLayer in ... seconds.\n\nEverything is restored to its previous state.\n\n >>> SecurityManagement.getSecurityManager().getUser()\n \n\n >>> app = ZopeTestCase.app()\n >>> user = getattr(app, ZopeTestCase.folder_name\n ... ).acl_users.getUserById(ZopeTestCase.user_name)\n >>> user.getRoles()\n ('test_role_1_', 'Authenticated')\n >>> ZopeTestCase.close(app)\n\nFinish tearing down the rest of the layers.\n\n >>> runner.tear_down_unneeded(options, [], setup_layers)\n Tear down collective.testcaselayer.ztc.ZTCLayer in ... seconds.\n\n.. -*-doctest-*-\n\ncollective.testcaselayer.layer\n==============================\n\nIn many cases, classes can be used as layers themselves where the base\nclasses are used as the base layers. This means that the layer\ninheritance herirarchy, used for code factoring and re-use, becomes\nbound to the layer set up heirachy, used to determine which layers are\nset up when and for which tests. IOW, it is not possible for a layer\nto subclass another layer *just* to re-use functionality without also\ndepending on that layer being set up as well. Additionally, when\nusing classes as layers, all layer methods (setUp, tearDown,\ntestSetUp, and testTearDown) must be defined on class layers with base\nclasses to avoid accidentally running the method of a base class/layer\nat the wrong time.\n\nThe collective.testcaselayer.layer module provides a Layer class\nintended to be used as a base class for classes whoss instances will\nbe layers. Instances of this class can also be used directly solely\nto group layers together into one layer.\n\n >>> from collective.testcaselayer import layer\n\nLayer Classes\n-------------\n\nUse the collective.testcaselayer.layer.Layer class to create your own\nlayer classes.\n\n >>> class FooLayer(layer.Layer):\n ... def setUp(self): print 'running FooLayer.setUp'\n\nThe instances of the class will be your actual zope.testing layer.\n\n >>> foo_layer = FooLayer()\n\n >>> from zope.testing.testrunner import runner\n >>> options = runner.get_options([], [])\n >>> runner.setup_layer(options, foo_layer, {})\n Set up FooLayer running FooLayer.setUp\n in ... seconds.\n\nBeware that the Layer class itself or subclasses can be used\nthemselves as layers without error but that is not how they're\nintended to be used. For example, using the FooLayer class as a layer\nwill treat the Layer base class as a layer itself and will set it up\nwhich is meaningless. Further, it will try to call the setUp method\nas a class method which will raise an error.\n\n >>> runner.setup_layer(options, FooLayer, {})\n Traceback (most recent call last):\n TypeError: unbound method setUp() must be called with FooLayer instance as first argument (got nothing instead)\n\nBase Layers\n-----------\n\nBase layers are designated by passing them into the layer class on\ninstantiation.\n\nCreate another layer class.\n\n >>> class BarLayer(layer.Layer):\n ... def setUp(self): print 'running BarLayer.setUp'\n\nCreate the new layer that uses foo_layer as a base layer.\n\n >>> bar_layer = BarLayer([foo_layer])\n\nSet up the layers.\n\n >>> runner.setup_layer(options, bar_layer, {})\n Set up FooLayer running FooLayer.setUp\n in ... seconds.\n Set up BarLayer running BarLayer.setUp\n in ... seconds.\n\nGrouping Layers\n---------------\n\nIf all that's required from a layer is that it groups other layers as\nbase layers, then the collective.testcaselayer.layer.Layer class can\nbe used directly.\n\nCreate another layer.\n\n >>> class BazLayer(layer.Layer):\n ... def setUp(self): print 'running BazLayer.setUp'\n >>> baz_layer = BazLayer()\n\nInstantiate the Layer class with the base layers, a module, and a name.\n\n >>> qux_layer = layer.Layer(\n ... [bar_layer, baz_layer],\n ... module='QuxModule', name='QuxLayer')\n\nSet up the layers.\n\n >>> runner.setup_layer(options, qux_layer, {})\n Set up FooLayer running FooLayer.setUp\n in ... seconds.\n Set up BarLayer running BarLayer.setUp\n in ... seconds.\n Set up BazLayer running BazLayer.setUp\n in ... seconds.\n Set up QuxModule.QuxLayer in ... seconds.\n\nBy default, layers have the same module and name as their class. If\nyou want the layer to have a different module or name than the class,\nthen the both can be passed in as arguments. This is useful in this\ncase and any time multiple instances of the same layer class will be\nused as layers.\n\nInstantiating the Layer class directly without passing a name raises\nan error.\n\n >>> layer.Layer([], module='QuxModule')\n Traceback (most recent call last):\n ValueError: The \"name\" argument is requied when instantiating\n \"Layer\" directly\n\nIf the Layer class is instantiated directly without passing a module,\nthe module name from the calling frame is used.\n\n >>> __name__ = 'BahModule'\n >>> quux_layer = layer.Layer([], name='QuuxLayer')\n >>> runner.setup_layer(options, quux_layer, {})\n Set up BahModule.QuuxLayer in ... seconds.\n\n.. -*-doctest-*-\n\ncollective.testcaselayer.sandbox\n================================\n\nSandboxed layers commit the changes made on setup to a sandboxed\nDemoStorage that uses the previous ZODB storage as a base storgae. On\ntear down, the layer will restore the base storage. This allows the\nlayer to use and commit changes to a fully functional ZODB while\nisolating the effects of the layer from any parent or sibling layers.\n\nAs one would expect, layers that use the sandboxed layer as a base\nlayer will see the ZODB according the base layer. Additionally,\nsandboxed layers can use other sandboxed layers as base layers, thus\nallowing for nested but isolated ZODB sandboxes.\n\nCreate a sandboxed layer. Layers that subclass Sandboxed should\nimplement an afterSetUp method to do any changes for the layer.\nAdditionally, such layers may also provide a beforeTearDown method to\ntear down any changes made by the layer that won't be cleaned up by\nrestoring the ZODB.\n\n >>> from collective.testcaselayer import ztc\n >>> class FooLayer(ztc.BaseZTCLayer):\n ... def afterSetUp(self):\n ... self.app.foo = 'foo'\n >>> foo_layer = FooLayer()\n\nBefore the layer is set up, the ZODB doesn't reflect the layer's\nchanges.\n\n >>> from Testing import ZopeTestCase\n >>> app = ZopeTestCase.app()\n >>> getattr(app, 'foo', None)\n >>> ZopeTestCase.close(app)\n\nAfter the layer is set up, the changes have been committed to the\nZODB.\n\n >>> foo_layer.setUp()\n\n >>> app = ZopeTestCase.app()\n >>> getattr(app, 'foo', None)\n 'foo'\n >>> ZopeTestCase.close(app)\n\nCreate a sandboxed layer that uses the first layer as a base layer.\n\n >>> class BarLayer(ztc.BaseZTCLayer):\n ... def afterSetUp(self):\n ... self.app.bar = 'bar'\n >>> bar_layer = BarLayer([foo_layer])\n\nBefore the sub-layer is set up, the ZODB still reflects the base\nlayer's changes but not the sub-layer's changes.\n\n >>> app = ZopeTestCase.app()\n >>> getattr(app, 'foo', None)\n 'foo'\n >>> getattr(app, 'bar', None)\n >>> ZopeTestCase.close(app)\n\nAfter the sub-layer is set up, the ZODB reflects the changes from both\nlayers.\n\n >>> bar_layer.setUp()\n\n >>> app = ZopeTestCase.app()\n >>> getattr(app, 'foo', None)\n 'foo'\n >>> getattr(app, 'bar', None)\n 'bar'\n >>> ZopeTestCase.close(app)\n\nAny test case using Testing.ZopeTestCase.sandbox.Sandboxed, such as\nzope.testbrowser tests run against Zope2, calls the ZopeLite.sandbox()\nfunction without any arguments. In such cases, the resulting per-test\nsandboxed ZODB will still be based on the layer sandboxed ZODB.\n\n >>> app = ZopeTestCase.Zope2.app(\n ... ZopeTestCase.Zope2.sandbox().open())\n >>> getattr(app, 'foo', None)\n 'foo'\n >>> getattr(app, 'bar', None)\n 'bar'\n >>> app._p_jar.close()\n\nAfter the sub-layer is torn down, the ZODB reflects only the changes\nfrom the base layer.\n\n >>> bar_layer.tearDown()\n\n >>> app = ZopeTestCase.app()\n >>> getattr(app, 'foo', None)\n 'foo'\n >>> getattr(app, 'bar', None)\n >>> ZopeTestCase.close(app)\n\nAfter the base layer is torn down, the ZODB doesn't reflect the changes\nfrom either layer.\n\n >>> foo_layer.tearDown()\n\n >>> app = ZopeTestCase.app()\n >>> getattr(app, 'foo', None)\n >>> getattr(app, 'bar', None)\n >>> ZopeTestCase.close(app)\n\n.. -*-doctest-*-\n\nFunctional and testbrowser testing patches\n==========================================\n\nTo use these patches, include the collective.testcaselayer\nconfigure.zcml. The patches address some bugs in\nTesting.ZopeTestCase.\n\nData streamed to the response\n-----------------------------\n\nDue to some behavior in Testing.ZopeTestCase.zopedoctest.functional,\nthe testbrowser.contents was empty when data had been streamed\ndirectly into the response (as opposed to returning the data from the\ncallable published). This made it difficult to do functional testing\nfor code that needed to stream data to the response for performance,\nsuch as when the response data is very large and would consume too\nmuch memory.\n\nStream iterators\n----------------\n\nA patch taken from plone.app.blob is also included so that HTTP\nresponses in the test environment support stream iterators. This\nallows functional testing of code that makes use of stream iterators.\n\nHTTP_REFERRER\n-------------\n\nDue to `bug #98437 `_,\n\"TestBrowser Referer: header set to 'localhost'\", some testbrowser\nrequests would raise NotFound. Two examples would be visiting the\nPlone login_form directly rather than following a link, or using the\nPlone content_status_history form.\n\nTest the Patches\n----------------\n\nAdd a document which renders the referer.\n\n >>> folder.addDTMLDocument(\n ... 'index_html', file='''\\\n ... \n ... \n ...
\n ...
\n ... link\n ... \n ... ''')\n ''\n\nOpen a browser.\n\n >>> from Products.Five.testbrowser import Browser\n >>> browser = Browser()\n >>> browser.handleErrors = False\n\nBefore patching, fresh requests have an invalid referer.\n\n >>> browser.open(folder.index_html.absolute_url())\n >>> print browser.contents\n \n localhost\n
\n
\n link\n \n\nAdd a script that streams content to the response.\n\n >>> from Products.PythonScripts import PythonScript\n >>> PythonScript.manage_addPythonScript(folder, 'foo.txt')\n ''\n >>> folder['foo.txt'].ZPythonScript_edit(params='', body='''\\\n ... context.REQUEST.response.setHeader('Content-Type', 'text/plain')\n ... context.REQUEST.response.setHeader(\n ... 'Content-Disposition',\n ... 'attachment;filename=foo.txt')\n ... context.REQUEST.response.write('foo')''')\n\nBefore patching, data streamed to the response is not in the browser\ncontents.\n\n >>> browser.open(folder['foo.txt'].absolute_url())\n >>> browser.isHtml\n False\n >>> print browser.contents\n\nAdd a script that returns a stream iterator.\n\n >>> from Products.PythonScripts import PythonScript\n >>> PythonScript.manage_addPythonScript(folder, 'bar.txt')\n ''\n >>> folder['bar.txt'].ZPythonScript_edit(params='', body='''\\\n ... from collective.testcaselayer.testing.iterator import (\n ... StreamIterator)\n ... context.REQUEST.response.setHeader('Content-Type', 'text/plain')\n ... context.REQUEST.response.setHeader(\n ... 'Content-Disposition',\n ... 'attachment;filename=bar.txt')\n ... return StreamIterator(['bar', 'qux'])''')\n\n >>> from AccessControl import allow_module\n >>> allow_module('collective.testcaselayer.testing.iterator')\n\nStream iterators are not supported.\n\n >>> browser.open(folder['bar.txt'].absolute_url())\n >>> browser.isHtml\n False\n >>> print browser.contents\n ['bar', 'qux']\n\nApply the patches.\n\n >>> from Products.Five import zcml\n >>> from Products.Five import fiveconfigure\n >>> from collective import testcaselayer\n >>> fiveconfigure.debug_mode = True\n >>> zcml.load_config('testing.zcml', package=testcaselayer)\n >>> fiveconfigure.debug_mode = False\n\nA fresh request should have no referer.\n\n >>> browser.open(folder.index_html.absolute_url())\n >>> print browser.contents\n \n
\n
\n link\n \n\nSubmitting a form via post should have no referer.\n\n >>> browser.getForm('post').submit()\n >>> print browser.contents\n \n
\n
\n link\n \n\nSubmitting a form via get should have no referer.\n\n >>> browser.getForm('get').submit()\n >>> print browser.contents\n \n
\n
\n link\n \n\nClicking a link should set the referer.\n\n >>> browser.getLink('link').click()\n >>> print browser.contents\n \n http://nohost/test_folder_1_/...\n
\n
\n link\n \n\nData streamed to the response is now in the browser contents.\n\n >>> browser.open(folder['foo.txt'].absolute_url())\n >>> browser.isHtml\n False\n >>> print browser.contents\n Status: 200 OK\n X-Powered-By: Zope (www.zope.org), Python (www.python.org)\n Content-Length: 0\n Content-Type: text/plain\n Content-Disposition: attachment;filename=foo.txt\n foo\n\nStream iterators are now in the browser contents.\n\n >>> browser.open(folder['bar.txt'].absolute_url())\n >>> browser.isHtml\n False\n >>> print browser.contents\n barqux\n\nChangelog\n=========\n\n1.6.1 (2015-08-14)\n------------------\n\n- Minor cleanup: whitespace, git ignores, setup.py.\n [gforcada, rnix, maurits]\n\n\n1.6 (2012-10-17)\n----------------\n\n* Don't break if the portal has no portal_kss tool.\n [davisagli]\n\n1.5 - 2012-05-18\n----------------\n\n* Plone 4.1 compatibility.\n [rossp]\n\n* Let layer tear down do cleanup. Allows post_mortem debugging to see\n the state in the TB before DB connections are closed or other\n cleanup is done.\n [rossp]\n\n1.4 - 2011-07-15\n----------------\n\n* Move the mock mail host to an actual GS profile so it won't be\n replaced when dependent layers run their own profiles. Also makes\n the mock mail host usable outside of tests.\n [rossp]\n\n* Add some utility methods for setting testbrowser AT calendar widgets\n without end/beginning-of-month intermittent failures.\n [rossp]\n\n* Avoid a version conflict between PloneTestCase and\n zope.testing/testrunner.\n [rossp]\n\n1.3 - 2010-02-09\n------------------\n\n* Add a loadZCML convenience method [rossp]\n\n* Add a common layer with some useful test setup [rossp]\n\n* Add a mock mail host layer [rossp]\n\n1.2.2 - 2009-11-14\n------------------\n\n* Add functional testing support for stream iterator responses. Taken\n from witsch's plone.app.blob testing patches.\n [rossp]\n\n* Zope 2.10-2.12 compatibility\n [rossp, witsch]\n\n* Fix `Sandboxed` replacement for Zope 2.12 / Plone 4.\n [witsch]\n\n1.2.1 - 2009-10-11\n------------------\n\n* Move the ZTC functional doctest monkey patches to testing.zcml so\n that they don't get picked up under auto-include. optilude reported\n this was breaking debug-mode.\n\n1.2 - 2009-08-21\n----------------\n\n* Add a patch so that data streamed to the response is available in\n testbrowser.contents. [rossp]\n* Add a patch for the HTTP_REFERER testbrowser bug.\n https://bugs.launchpad.net/bugs/98437 [rossp]\n\n1.1 - 2009-07-29\n----------------\n\n* Fix release. Files were missing due to the setuptools interaction\n with SVN 1.6.\n\n1.0 - 2009-07-29\n----------------\n\n* Tested against Plone 3.3rc4\n\n* Add sample code for basic Plone test case layers\n\n* Deprecate zope.testing<3.6 support\n\n* The collective.testcaselayer.ptc module needs to call\n ptc.setupPloneSite() in order to make sure the plone site exists\n\n0.2 - 2008-01-08\n----------------\n\n* Make the self.folder attribute available in PortalTestCase\n sub-layers\n* Make tests compatible with zope.testing.testrunner refactoring\n\n0.1 - 2008-05-23\n----------------\n\n* Initial release\n\n\nTODO\n====\n\n* Add convenience method for loading ZCML. (witsch)\n\n* Factor out collective.testclasslayer.layer\n\nThe collective.testclasslayer.layer module doesn't actually have\nanything to do with test cases, but I didn't want to create a separate\npackage just for this one bit. If someone wants to put this in\nzope.testing or some other common testing dependency, that would be\ngreat.\n\n* Factor unittest.TestCase out of Testing.ZopeTestCase.base.TestCase\n\nIt might be appropriate to refactor out the ZTC specific pieces of the\ntest cases in the Testing.ZopeTestCase package such that there is a\ncommon base class that doesn't subclass unittest.TestCase. With this\nin place we could do away with collective.testcaselayer.testcase and\nhave common base classes that could be used either as layers or as\ntest cases.", "description_content_type": null, "docs_url": null, "download_url": null, "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://pypi.python.org/pypi/collective.testcaselayer", "keywords": null, "license": "GPL", "maintainer": null, "maintainer_email": null, "name": "collective.testcaselayer", "package_url": "https://pypi.org/project/collective.testcaselayer/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/collective.testcaselayer/", "project_urls": { "Homepage": "http://pypi.python.org/pypi/collective.testcaselayer" }, "release_url": "https://pypi.org/project/collective.testcaselayer/1.6.1/", "requires_dist": null, "requires_python": null, "summary": "Use test cases as zope.testing layers", "version": "1.6.1" }, "last_serial": 1677621, "releases": { "0.1": [ { "comment_text": "", "digests": { "md5": "8aa8dc96b3ddf4eb44b57d6ca6404e2f", "sha256": "2420d047bf228d6906ea61c94ba30c68ff91af32d7f452f298da9aeec8002649" }, "downloads": -1, "filename": "collective.testcaselayer-0.1.tar.gz", "has_sig": false, "md5_digest": "8aa8dc96b3ddf4eb44b57d6ca6404e2f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 18985, "upload_time": "2008-05-24T06:11:58", "url": "https://files.pythonhosted.org/packages/7c/6b/5006ca09411b6019159e4dea3111ffbbae5b32c8f5f305c0546193c34138/collective.testcaselayer-0.1.tar.gz" } ], "0.2": [ { "comment_text": "", "digests": { "md5": "9471b6baff1655a6cbe07f4f6cc8d465", "sha256": "813be926fcbb88fb501b8addd7a7e5d659c7931c3ac296db4c130b8888f78e68" }, "downloads": -1, "filename": "collective.testcaselayer-0.2.tar.gz", "has_sig": false, "md5_digest": "9471b6baff1655a6cbe07f4f6cc8d465", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19749, "upload_time": "2009-01-08T22:17:06", "url": "https://files.pythonhosted.org/packages/a3/65/0d258f3039ae9e4b569e8d9ccee3d41a5f2a58da9ed7911dae96de109ef1/collective.testcaselayer-0.2.tar.gz" } ], "1.0": [ { "comment_text": "", "digests": { "md5": "6cda7ac6ee3f58942c794109053ac469", "sha256": "8f3f23bf00fb9c2789cc1f0105efb555de1d55189fc72578ee81fc4c7a42a87a" }, "downloads": -1, "filename": "collective.testcaselayer-1.0.tar.gz", "has_sig": false, "md5_digest": "6cda7ac6ee3f58942c794109053ac469", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16034, "upload_time": "2009-07-30T01:30:32", "url": "https://files.pythonhosted.org/packages/2f/46/8ba0a3c7f40661ceae3b43ac2e61b06f6e0d8b0dad11f7e236c3114aff58/collective.testcaselayer-1.0.tar.gz" } ], "1.1": [ { "comment_text": "", "digests": { "md5": "607173c59e839353e35bf9bcb6cfa79d", "sha256": "294043e387b7a24246b0baf950e5f99a36abaf19f6028fdd9fb1fd467ea0b160" }, "downloads": -1, "filename": "collective.testcaselayer-1.1.tar.gz", "has_sig": false, "md5_digest": "607173c59e839353e35bf9bcb6cfa79d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 28040, "upload_time": "2009-08-22T08:26:03", "url": "https://files.pythonhosted.org/packages/52/4f/35892aa33131342b8a2b114bbbd6a14c3cc9e269833de65f114b0d438c0f/collective.testcaselayer-1.1.tar.gz" } ], "1.2": [ { "comment_text": "", "digests": { "md5": "2c01c3684af6b7c7fa18d7d909665990", "sha256": "86b33e4ac91eea77e27f6f30bf747db6bfc33e8da51b16922a3976ca35c2b071" }, "downloads": -1, "filename": "collective.testcaselayer-1.2.tar.gz", "has_sig": false, "md5_digest": "2c01c3684af6b7c7fa18d7d909665990", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 28038, "upload_time": "2009-08-22T08:27:05", "url": "https://files.pythonhosted.org/packages/c8/ad/7c3b0fb58ea9181bac077195952226e35ab20b83e5cb8490f1b3df898c32/collective.testcaselayer-1.2.tar.gz" } ], "1.2.1": [ { "comment_text": "", "digests": { "md5": "b77d91f31ecf4bd861e367a3cc7b91b5", "sha256": "015c48bac05caff07efa940f02363cf8737a6d65c36f03b01e2eb3a5f22982f1" }, "downloads": -1, "filename": "collective.testcaselayer-1.2.1.tar.gz", "has_sig": false, "md5_digest": "b77d91f31ecf4bd861e367a3cc7b91b5", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 29619, "upload_time": "2009-10-11T20:26:01", "url": "https://files.pythonhosted.org/packages/c5/71/dbc322fcb50ce7304a3b1238c269cb6e10d314aed8f1a4280cfbdbb92f51/collective.testcaselayer-1.2.1.tar.gz" } ], "1.2.2": [ { "comment_text": "", "digests": { "md5": "a595191c27cfe4a94208863bd1ce0778", "sha256": "5ee720c8647290f9af1fd9ae1fd57e78c91652ebf2005af9271c458b6e53fae4" }, "downloads": -1, "filename": "collective.testcaselayer-1.2.2.tar.gz", "has_sig": false, "md5_digest": "a595191c27cfe4a94208863bd1ce0778", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 31512, "upload_time": "2009-11-15T07:39:42", "url": "https://files.pythonhosted.org/packages/4d/e2/9d9fa6930c607da03ac11632cde6068b0ee9ecf425fe3d39cd1123c2c8e5/collective.testcaselayer-1.2.2.tar.gz" } ], "1.3": [ { "comment_text": "", "digests": { "md5": "921e2333c62656563a652171f63dd707", "sha256": "9abe53c74390dea6a11e6e0f8d736cd3d94a9bc61e006baf04f7652ce185161f" }, "downloads": -1, "filename": "collective.testcaselayer-1.3.zip", "has_sig": false, "md5_digest": "921e2333c62656563a652171f63dd707", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 51021, "upload_time": "2010-02-10T07:45:58", "url": "https://files.pythonhosted.org/packages/cd/1d/df601c2c6b1f562b8e711efb3fc2fc14db96f23dc897866d61b2fb624cf4/collective.testcaselayer-1.3.zip" } ], "1.4": [ { "comment_text": "", "digests": { "md5": "99db0186a638d5548e827be1fe663f1a", "sha256": "4dce9c26779a1741b6344d0e6bdd7370d696118a29aab05932fdf2009e6af0a8" }, "downloads": -1, "filename": "collective.testcaselayer-1.4.tar.gz", "has_sig": false, "md5_digest": "99db0186a638d5548e827be1fe663f1a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 36294, "upload_time": "2011-07-15T20:33:14", "url": "https://files.pythonhosted.org/packages/80/b6/9a2c680002cbbe8e0c328c0587bc82d9728fad9c3b08010b6e0a783f8723/collective.testcaselayer-1.4.tar.gz" } ], "1.5": [ { "comment_text": "", "digests": { "md5": "8720c97c749869f97a5a77798e89f7d3", "sha256": "32b4b50dbb29dff01be81d09e67f5035591262c14f61360c9cd4a2c975478921" }, "downloads": -1, "filename": "collective.testcaselayer-1.5.tar.gz", "has_sig": false, "md5_digest": "8720c97c749869f97a5a77798e89f7d3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 37554, "upload_time": "2012-05-19T00:00:47", "url": "https://files.pythonhosted.org/packages/fa/53/b38fd33c8315a53bb6a3e538056b7807eb73e9282adcec9efd14b081b9b6/collective.testcaselayer-1.5.tar.gz" } ], "1.6": [ { "comment_text": "", "digests": { "md5": "fd8387d6b6ebd8645ec92f5f1e512450", "sha256": "9824db65e4738491901589ddfe658b4ebb50080e308e07f9d22ce156707e3518" }, "downloads": -1, "filename": "collective.testcaselayer-1.6.zip", "has_sig": false, "md5_digest": "fd8387d6b6ebd8645ec92f5f1e512450", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 55445, "upload_time": "2012-10-18T20:42:51", "url": "https://files.pythonhosted.org/packages/a0/81/a6ecb068154903387efcc2b0d68ad0e91520606b71944dfcc4939b0d49cd/collective.testcaselayer-1.6.zip" } ], "1.6.1": [ { "comment_text": "", "digests": { "md5": "b93ce850fbe68a008d9a992606f5a92b", "sha256": "d201d9bb2270ce6f52109204e552399f2a8d5001ac25c63cb3e78888a825d30d" }, "downloads": -1, "filename": "collective.testcaselayer-1.6.1.tar.gz", "has_sig": false, "md5_digest": "b93ce850fbe68a008d9a992606f5a92b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 37189, "upload_time": "2015-08-14T15:52:52", "url": "https://files.pythonhosted.org/packages/99/21/4992d18293eaadc403e686dd8e5497f892b06f4f010d783ef7e4d9f8e97b/collective.testcaselayer-1.6.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "b93ce850fbe68a008d9a992606f5a92b", "sha256": "d201d9bb2270ce6f52109204e552399f2a8d5001ac25c63cb3e78888a825d30d" }, "downloads": -1, "filename": "collective.testcaselayer-1.6.1.tar.gz", "has_sig": false, "md5_digest": "b93ce850fbe68a008d9a992606f5a92b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 37189, "upload_time": "2015-08-14T15:52:52", "url": "https://files.pythonhosted.org/packages/99/21/4992d18293eaadc403e686dd8e5497f892b06f4f010d783ef7e4d9f8e97b/collective.testcaselayer-1.6.1.tar.gz" } ] }