From: Martin A. <svn...@pl...> - 2010-07-04 12:58:23
|
Author: optilude Date: Sun Jul 4 12:58:15 2010 New Revision: 37627 Modified: plone.testing/trunk/README.txt plone.testing/trunk/src/plone/testing/layer.py plone.testing/trunk/src/plone/testing/layer.txt plone.testing/trunk/src/plone/testing/z2.py plone.testing/trunk/src/plone/testing/z2.txt Log: Refactor the way we manage the functional/integration testing separation. By separating the 'fixture' layers from those strictly involved in managing the test lifecycle, we can avoid having to tear down and re-set-up layers when switching from integration to functional testing. Modified: plone.testing/trunk/README.txt ============================================================================== --- plone.testing/trunk/README.txt (original) +++ plone.testing/trunk/README.txt Sun Jul 4 12:58:15 2010 @@ -1726,6 +1726,11 @@ On set-up, the layer will configure a Zope environment with: +**Note:** The ``STARTUP`` layer is a useful base layer for your own fixtures, +but should not be used directly, since it provides no test lifecycle or +transaction management. See the "Integration test" and "Functional" test +sections below for examples of how to create your own layers. + * Debug mode enabled. * ZEO client cache disabled. * Some patches installed, which speed up Zope startup by disabling the help @@ -1764,7 +1769,9 @@ | | ``request`` | +------------+--------------------------------------------------+ -This layer is intended for integration testing. +This layer is intended for integration testing against the simple ``STARTUP`` +fixture. If you want to create your own layer with a more advanced, shared +fixture, see "Integration and functional testing with custom fixtures" below. For each test, it exposes the Zope application root as the resource ``app``. This is wrapped in the request container, so you can do ``app.REQUEST`` to @@ -1807,6 +1814,10 @@ | | ``request`` | +------------+--------------------------------------------------+ +This layer is intended for functional testing against the simple ``STARTUP`` +fixture. If you want to create your own layer with a more advanced, shared +fixture, see "Integration and functional testing with custom fixtures" below. + As its name implies, this layer is intended mainly for functional end-to-end testing using tools like `zope.testbrowser`_. See also the ``Browser`` object as described under "Helper functions" below. @@ -1817,25 +1828,87 @@ a stacked ``DemoStorage`` for each test. This is slower, but allows test code to perform and explicit commit, as will usually happen in a functional test. -HTTP ZServer thread -~~~~~~~~~~~~~~~~~~~ +Integration and functional testing with custom fixtures +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you want to extend the ``STARTUP`` fixture for use with integration or +functional testing, you should use the following pattern: + +* Create a layer class and a "fixture" base layer instance that has + ``z2.STARTUP`` (or some intermediary layer, such as ``z2.ZSERVER_FIXTURE`` + or ``z2.FTP_SERVER_FIXTURE``, shown below) as a base. +* Create "end user" layers by instantiating the ``z2.IntegrationTesting`` + and/or ``FunctionalTesting`` classes with this new "fixture" layer as a + base. + +This allows the same fixture to be used regardless of the "style" of testing, +minimising the amount of set-up and tear-down. The "fixture" layers manage the +fixture as part of the *layer* lifecycle. The layer class +(``IntegrationTesting`` or ``FunctionalTesting``), manages the *test* +lifecycle, and the test lifecycle only. + +For example:: + + from plone.testing import Layer, z2, zodb + + class MyLayer(Layer): + defaultBases = (z2.STARTUP,) + + def setUp(self): + # Set up the fixture here + ... + + def tearDown(self): + # Tear down the fixture here + ... + + MY_FIXTURE = MyLayer() + + MY_INTEGRATION_TESTING = z2.IntegrationTesting(bases=(MY_FIXTURE,), name="MyFixture:Integration") + MY_FUNCTIONAL_TESTING = z2.FunctionalTesting(bases=(MY_FIXTURE,), name="MyFixture:Functional") + +(Note that we need to give an explicit, unique name to the two layers that +re-use the ``IntegrationTesting`` and ``FunctionalTesting`` classes.) + +In this example, other layers could extend the "MyLayer" fixture by using +``MY_FIXTURE`` as a base. Tests would use either ``MY_INTEGRATION_TESTING`` +or ``MY_FUNCTIONAL_TESTING`` as appropriate. However, even if both these two +layers were used, the fixture in ``would``MY_FIXTURE`` only be set up once. + + **Note:** If you implement the ``testSetUp()`` and ``testTearDown()`` test + lifecycle methods in your "fixture" layer (e.g. in the the ``MyLayer`` + class above), they will execute before the corresponding methods from + ``IntegrationTesting`` and ``FunctionalTesting``. Hence, they cannot use + those layers' resources (``app`` and ``request``). + +It may be preferable, therefore, to have your own "test lifecycle" layer +classes that subclass ``IntegrationTesting`` and/or ``FunctionalTesting`` and +call base class methods as appropriate. ``plone.app.testing`` takes this +approach, for example. + +HTTP ZServer thread (fixture only) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +------------+--------------------------------------------------+ -| Layer: | ``plone.testing.z2.ZSERVER`` | +| Layer: | ``plone.testing.z2.ZSERVER_FIXTURE`` | +------------+--------------------------------------------------+ | Class: | ``plone.testing.z2.ZServer`` | +------------+--------------------------------------------------+ -| Bases: | ``plone.testing.z2.FUNCTIONAL`` | +| Bases: | ``plone.testing.z2.STARTUP`` | +------------+--------------------------------------------------+ | Resources: | ``host`` | | +--------------------------------------------------+ | | ``port`` | +------------+--------------------------------------------------+ -This layer extends the ``z2.FUNCTINAL`` layer to start the Zope HTTP server in +This layer extends the ``z2.STARTUP`` layer to start the Zope HTTP server in a separate thread. This means the test site can be accessed through a web browser, and can thus be used with tools like `Windmill`_ or `Selenium`_. + **Note:** This layer is useful as a fixture base layer only, because it does + not manage the test lifecycle. Use the ``ZSERVER`` layer if you want to + execute functional tests against this fixture. + The ZServer's hostname (normally ``localhost``) is available through the resource ``host``, whilst the port it is running on is available through the resource ``port``. @@ -1844,23 +1917,49 @@ site through a web browser. The default URL will be ``http://localhost:55001``. -FTP server thread -~~~~~~~~~~~~~~~~~ +HTTP ZServer functional testing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +------------+--------------------------------------------------+ -| Layer: | ``plone.testing.z2.FTP_SERVER`` | +| Layer: | ``plone.testing.z2.ZSERVER`` | ++------------+--------------------------------------------------+ +| Class: | ``plone.testing.z2.FunctionalTesting`` | ++------------+--------------------------------------------------+ +| Bases: | ``plone.testing.z2.ZSERVER_FIXTURE`` | ++------------+--------------------------------------------------+ +| Resources: | | ++------------+--------------------------------------------------+ + +This layer provides the functional testing lifecycle against the fixture set +up by the ``z2.ZSERVER_FIXTURE`` layer. + +You can use this to run "live" functional tests against a basic Zope site. +You should **not** use it as a base. Instead, create your own "fixture" +layer that extends ``z2.ZSERVER_FIXTURE``, and then instantiate the +``FunctionalTesting`` class with this extended fixture layer as a base, +as outlined above. + +FTP server thread (fixture only) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++------------+--------------------------------------------------+ +| Layer: | ``plone.testing.z2.FTP_SERVER_FIXTURE`` | +------------+--------------------------------------------------+ | Class: | ``plone.testing.z2.FTPServer`` | +------------+--------------------------------------------------+ -| Bases: | ``plone.testing.z2.FUNCTIONAL`` | +| Bases: | ``plone.testing.z2.STARTUP`` | +------------+--------------------------------------------------+ | Resources: | ``host`` | | +--------------------------------------------------+ | | ``port`` | +------------+--------------------------------------------------+ -This layer is the FTP server equivalent of the ``ZSERVER`` layer. It can be -used to functionally test Zope servers. +This layer is the FTP server equivalent of the ``ZSERVER_FIXTURE`` layer. It +can be used to functionally test Zope servers. + + **Note:** This layer is useful as a fixture base layer only, because it does + not manage the test lifecycle. Use the ``FTP_SERVER`` layer if you want to + execute functional tests against this fixture. *Hint:* Whilst the layer is set up, you can actually access the test Zope site through an FTP client. The default URL will be @@ -1875,6 +1974,28 @@ and close down two servers on different ports. They will then share a main loop. +FTP server functional testing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++------------+--------------------------------------------------+ +| Layer: | ``plone.testing.z2.FTP_SERVER`` | ++------------+--------------------------------------------------+ +| Class: | ``plone.testing.z2.FunctionalTesting`` | ++------------+--------------------------------------------------+ +| Bases: | ``plone.testing.z2.FTP_SERVER_FIXTURE`` | ++------------+--------------------------------------------------+ +| Resources: | | ++------------+--------------------------------------------------+ + +This layer provides the functional testing lifecycle against the fixture set +up by the ``z2.FTP_SERVER_FIXTURE`` layer. + +You can use this to run "live" functional tests against a basic Zope site. +You should **not** use it as a base. Instead, create your own "fixture" +layer that extends ``z2.FTP_SERVER_FIXTURE``, and then instantiate the +``FunctionalTesting`` class with this extended fixture layer as a base, +as outlined above. + Helper functions ~~~~~~~~~~~~~~~~ Modified: plone.testing/trunk/src/plone/testing/layer.py ============================================================================== --- plone.testing/trunk/src/plone/testing/layer.py (original) +++ plone.testing/trunk/src/plone/testing/layer.py Sun Jul 4 12:58:15 2010 @@ -151,16 +151,20 @@ """ if self.__class__ is Layer and name is None: - raise ValueError('The "name" argument is required when instantiating `Layer` directly') + raise ValueError('The `name` argument is required when instantiating `Layer` directly') if name is None and bases is not None: - raise ValueError('The "name" argument is required when overriding bases with the `bases` argument') + raise ValueError('The `name`` argument is required when overriding bases with the `bases` argument') super(Layer, self).__init__() if bases is None: bases = self.defaultBases - self.__bases__ = tuple(bases) + + try: + self.__bases__ = tuple(bases) + except (KeyError, TypeError,): + raise ValueError('The `bases` argument must be a sequence.') if name is None: name = self.__class__.__name__ Modified: plone.testing/trunk/src/plone/testing/layer.txt ============================================================================== --- plone.testing/trunk/src/plone/testing/layer.txt (original) +++ plone.testing/trunk/src/plone/testing/layer.txt Sun Jul 4 12:58:15 2010 @@ -53,7 +53,7 @@ >>> Layer((SIMPLE_LAYER,)) Traceback (most recent call last): ... - ValueError: The "name" argument is required when instantiating `Layer` directly + ValueError: The `name` argument is required when instantiating `Layer` directly >>> class NullLayer(Layer): ... pass Modified: plone.testing/trunk/src/plone/testing/z2.py ============================================================================== --- plone.testing/trunk/src/plone/testing/z2.py (original) +++ plone.testing/trunk/src/plone/testing/z2.py Sun Jul 4 12:58:15 2010 @@ -592,11 +592,26 @@ class IntegrationTesting(Layer): """This layer extends ``STARTUP`` to add rollback of the transaction - after each test. + after each test. It does not manage a fixture and has no layer lifecyle, + only a test lifecycle. The application root is available as the resource ``app`` and the request is available as the resource ``request``, set up and torn down for each test. + + Hint: If you want to create your own fixture on top of ``STARTUP``, + create a new layer that has ``STARTUP`` as a base. Then instantiate + this layer with your new "fixture" layer as a base, e.g.:: + + from plone.testing import z2 + from plone.testing import Layer + + class MyFixture(Layer): + + ... + + MY_FIXTURE = MyFixture(bases=(z2.STARTUP,), name='MyFixture') + MY_INTEGRATION_TESTING = z2.IntegrationTesting(bases=(MY_FIXTURE,), name='MyFixture:Integration') """ defaultBases = (STARTUP,) @@ -648,6 +663,20 @@ As with ``INTEGRATION_TESTING``, the application root is available as the resource ``app`` and the request is available as the resource ``request``, set up and torn down for each test. + + Hint: If you want to create your own fixture on top of ``STARTUP``, + create a new layer that has ``STARTUP`` as a base. Then instantiate + this layer with your new "fixture" layer as a base, e.g.:: + + from plone.testing import z2 + from plone.testing import Layer + + class MyFixture(Layer): + + ... + + MY_FIXTURE = MyFixture(bases=(z2.STARTUP,), name='MyFixture') + MY_FUNCTIONAL_TESTING = z2.FunctinoalTesting(bases=(MY_FIXTURE,), name='MyFixture:Functional') """ defaultBases = (STARTUP,) @@ -704,16 +733,20 @@ class ZServer(Layer): """Start a ZServer that accesses the fixture managed by the - ``FUNCTIONAL_TESTING`` layer. + ``STARTUP`` layer. The host and port are available as the resources ``host`` and ``port``, respectively. This should *not* be used in parallel with the ``FTP_SERVER`` layer, since it shares the same async loop. + + The ``ZSERVER_FIXTURE`` layer must be used as the base for a layer that + uses the ``FunctionalTesting`` layer class. The ``ZSERVER`` layer is + an example of such a layer. """ - defaultBases = (FUNCTIONAL_TESTING,) + defaultBases = (STARTUP,) host = 'localhost' port = 55001 @@ -795,7 +828,12 @@ while socket_map and not self._shutdown: asyncore.poll(self.timeout, socket_map) -ZSERVER = ZServer() +# Fixture layer - use as a base layer, but don't use directly, as it has no +# test lifecycle +ZSERVER_FIXTURE = ZServer() + +# Functional testing layer that uses the ZSERVER_FIXTURE +ZSERVER = FunctionalTesting(bases=(ZSERVER_FIXTURE,), name="ZServer:Functional") class FTPServer(ZServer): """FTP variant of the ZServer layer. @@ -805,9 +843,13 @@ layer class (like this layer class does) and implement setUpServer() and tearDownServer() to set up and close down two servers on different ports. They will then share a main loop. + + The ``FTP_SERVER_FIXTURE`` layer must be used as the base for a layer that + uses the ``FunctionalTesting`` layer class. The ``FTP_SERVER`` layer is + an example of such a layer. """ - defaultBases = (FUNCTIONAL_TESTING,) + defaultBases = (STARTUP,) host = 'localhost' port = 55002 @@ -836,4 +878,9 @@ """ self.ftpServer.close() -FTP_SERVER = FTPServer() +# Fixture layer - use as a base layer, but don't use directly, as it has no +# test lifecycle +FTP_SERVER_FIXTURE = FTPServer() + +# Functional testing layer that uses the FTP_SERVER_FIXTURE +FTP_SERVER = FunctionalTesting(bases=(FTP_SERVER_FIXTURE,), name="FTPServer:Functional") Modified: plone.testing/trunk/src/plone/testing/z2.txt ============================================================================== --- plone.testing/trunk/src/plone/testing/z2.txt (original) +++ plone.testing/trunk/src/plone/testing/z2.txt Sun Jul 4 12:58:15 2010 @@ -18,7 +18,8 @@ pristine environment. **Note**: You should probably use at least ``INTEGRATION_TESTING`` for any -real test. +real test, although ``STARTUP`` is a useful base layer if you are setting up +your own fixture. See the description of ``INTEGRATION_TESTING`` below. >>> "%s.%s" % (z2.STARTUP.__module__, z2.STARTUP.__name__,) 'plone.testing.z2.Startup' @@ -26,7 +27,7 @@ >>> z2.STARTUP.__bases__ (<Layer 'plone.testing.zca.LayerCleanup'>,) -On layer setup, Zope is initialised using a lightweight process. This involves +On layer setup, Zope is initialised in a lightweight manner. This involves certain patches to global modules that Zope manages, to reduce setup time, a database based on ``DemoStorage``, and a minimal set of products that must be installed for Zope 2 to work. A minimal set of ZCML is loaded, but packages @@ -109,7 +110,7 @@ from plone.testing import Layer, z2, zodb class MyLayer(Layer): - defaultBases = (INTEGRATION_TESTING,) # see below + defaultBases = (z2.STARTUP,) def setUp(self): self['zodbDB'] = zodb.stackDemoStorage(self.get('zodbDB'), name='MyLayer') @@ -128,11 +129,11 @@ def tearDown(self): self['zodbDB'].close() del self['zodbDB'] - + Note that you would normally *not* use the ``z2.zopeApp()`` in a test or in a -``testSetUp()`` or ``testTearDown()`` method. The ``INTEGRATION_TESTING`` and -``FUNCTIONAL_TESTING`` layers manage the application object for you, exposing -them as the resource ``app`` (see below). +``testSetUp()`` or ``testTearDown()`` method. The ``IntegrationTesting`` and +``FunctionalTesting`` layer classes manage the application object for you, +exposing them as the resource ``app`` (see below). After layer setup, the global component registry contains a number of components needed by Zope. @@ -150,7 +151,8 @@ The ``STARTUP`` layer does not perform any specific test setup or tear-down. That is left up to the ``INTEGRATION_TESTING`` and ``FUNCTIONAL_TESTING`` -layers. +layers, or other layers using their layer classes - ``IntegrationTesting`` +and ``FunctionalTesting``. >>> z2.STARTUP.testSetUp() >>> z2.STARTUP.testTearDown() @@ -175,10 +177,16 @@ Integration test ~~~~~~~~~~~~~~~~ -``INTEGRATION_TESTING`` is intended for Zope 2 integration testing. It extends -``STARTUP`` to ensure that a transaction is begun before and rolled back after -each test. Two resources, ``app`` and ``request``, are available during -testing as well. +``INTEGRATION_TESTING`` is intended for simple Zope 2 integration testing. It +extends ``STARTUP`` to ensure that a transaction is begun before and rolled +back after each test. Two resources, ``app`` and ``request``, are available +during testing as well. It does not manage any layer state - it implements +the test lifecycle methods only. + +**Note:** You would normally *not* use ``INTEGRATION_TESTING`` as a base +layer. Instead, you'd use the ``IntegrationTesting`` class to create your +own layer with the testing lifecycle semantics of ``INTEGRATION_TESTING``. +See the ``plone.testing`` ``README`` file for an example. ``app`` is the application root. In a test, you should use this instead of the ``zopeApp`` context manager (which remains the weapon of choice for @@ -283,6 +291,11 @@ explicit commit, which is usually required for end-to-end testing. The downside is that the set-up and tear-down of each test takes longer. +**Note:** Again, you would normally *not* use ``FUNCTIONAL_TESTING`` as a base +layer. Instead, you'd use the ``FunctionalTesting`` class to create your own +layer with the testing lifecycle semantics of ``FUNCTIONAL_TESTING``. See +the ``plone.testing`` ``README`` file for an example. + Like ``INTEGRATION_TESTING``, ``FUNCTIONAL_TESTING`` is based on ``STARTUP``. >>> "%s.%s" % (z2.FUNCTIONAL_TESTING.__module__, z2.FUNCTIONAL_TESTING.__name__,) @@ -341,13 +354,15 @@ The test browser ~~~~~~~~~~~~~~~~ -The ``FUNCTIONAL_TESTING`` layer is also the basis for functional testing -using ``zope.testbrowser``. This simulates a web browser, allowing an -application to be tested "end-to-end" via its user-facing interface. - -To use the test browser with the ``FUNCTIONAL_TESTING`` layer, we need to use -a custom browser client, which ensures that the test browser uses the correct -ZODB and is appropriately isolated from the test code. +The ``FUNCTIONAL_TESTING`` layer and ``FunctionalTesting`` layer class are +the basis for functional testing using ``zope.testbrowser``. This simulates a +web browser, allowing an application to be tested "end-to-end" via its +user-facing interface. + +To use the test browser with a ``FunctionalTesting`` layer (such as the +default ``FUNCTIONAL_TESTING`` layer instance), we need to use a custom +browser client, which ensures that the test browser uses the correct ZODB and +is appropriately isolated from the test code. >>> options = runner.get_options([], []) >>> setupLayers = {} @@ -407,24 +422,33 @@ HTTP server ~~~~~~~~~~~ -The ``ZSERVER`` layer extends ``FUNCTIONAL_TESTING`` to start up a -single-threaded Zope server in a separate thread. This makes it possible to -connect to the test instance using a web browser or a testing tool like -Selenium or Windmill. +The ``ZSERVER_FIXTURE`` layer extends ``STARTUP`` to start a single-threaded +Zope server in a separate thread. This makes it possible to connect to the +test instance using a web browser or a testing tool like Selenium or Windmill. - >>> "%s.%s" % (z2.ZSERVER.__module__, z2.ZSERVER.__name__,) +The ``ZSERVER`` layer provides a ``FunctionalTesting`` layer that has +``ZSERVER_FIXTURE`` as its base. + + >>> "%s.%s" % (z2.ZSERVER_FIXTURE.__module__, z2.ZSERVER_FIXTURE.__name__,) 'plone.testing.z2.ZServer' + >>> z2.ZSERVER_FIXTURE.__bases__ + (<Layer 'plone.testing.z2.Startup'>,) + + + >>> "%s.%s" % (z2.ZSERVER.__module__, z2.ZSERVER.__name__,) + 'plone.testing.z2.ZServer:Functional' + >>> z2.ZSERVER.__bases__ - (<Layer 'plone.testing.z2.FunctionalTesting'>,) + (<Layer 'plone.testing.z2.ZServer'>,) >>> options = runner.get_options([], []) >>> setupLayers = {} >>> runner.setup_layer(options, z2.ZSERVER, setupLayers) Set up plone.testing.zca.LayerCleanup in ... seconds. Set up plone.testing.z2.Startup in ... seconds. - Set up plone.testing.z2.FunctionalTesting in ... seconds. Set up plone.testing.z2.ZServer in ... seconds. + Set up plone.testing.z2.ZServer:Functional in ... seconds. After layer setup, the resources ``host`` and ``port`` are available, and indicate where Zope is running. @@ -489,8 +513,8 @@ When the server is torn down, the ZServer thread is stopped. >>> runner.tear_down_unneeded(options, [], setupLayers) + Tear down plone.testing.z2.ZServer:Functional in ... seconds. Tear down plone.testing.z2.ZServer in ... seconds. - Tear down plone.testing.z2.FunctionalTesting in ... seconds. Tear down plone.testing.z2.Startup in ... seconds. Tear down plone.testing.zca.LayerCleanup in ... seconds. @@ -503,7 +527,8 @@ ~~~~~~~~~~ The ``FTP_SERVER`` layer is identical similar to ``ZSERVER``, except that it -starts an FTP server instead of an HTTP server. +starts an FTP server instead of an HTTP server. The fixture is contained in +the ``FTP_SERVER_FIXTURE`` layer. **Warning:** It is generally not safe to run the ``ZSERVER`` and ``FTP_SERVER`` layers concurrently, because they both start up the same @@ -512,21 +537,30 @@ and overriding the ``setUpServer()`` and ``tearDownServer()`` hooks to set up and close both servers. See the code for an example. -The ``FTP_SERVER`` layer is based on the ``FUNCTIONAL_TESTING`` layer. +The ``FTP_SERVER_FIXTURE`` layer is based on the ``STARTUP`` layer. - >>> "%s.%s" % (z2.FTP_SERVER.__module__, z2.FTP_SERVER.__name__,) + >>> "%s.%s" % (z2.FTP_SERVER_FIXTURE.__module__, z2.FTP_SERVER_FIXTURE.__name__,) 'plone.testing.z2.FTPServer' + >>> z2.FTP_SERVER_FIXTURE.__bases__ + (<Layer 'plone.testing.z2.Startup'>,) + +The ``FTP_SERVER`` layer is based on ``FTP_SERVER_FIXTURE``, using the +``FunctionalTesting`` layer class. + + >>> "%s.%s" % (z2.FTP_SERVER.__module__, z2.FTP_SERVER.__name__,) + 'plone.testing.z2.FTPServer:Functional' + >>> z2.FTP_SERVER.__bases__ - (<Layer 'plone.testing.z2.FunctionalTesting'>,) + (<Layer 'plone.testing.z2.FTPServer'>,) >>> options = runner.get_options([], []) >>> setupLayers = {} >>> runner.setup_layer(options, z2.FTP_SERVER, setupLayers) Set up plone.testing.zca.LayerCleanup in ... seconds. Set up plone.testing.z2.Startup in ... seconds. - Set up plone.testing.z2.FunctionalTesting in ... seconds. Set up plone.testing.z2.FTPServer in ... seconds. + Set up plone.testing.z2.FTPServer:Functional in ... seconds. After layer setup, the resources ``host`` and ``port`` are available, and indicate where Zope is running. @@ -605,8 +639,8 @@ When the server is torn down, the FTP thread is stopped. >>> runner.tear_down_unneeded(options, [], setupLayers) + Tear down plone.testing.z2.FTPServer:Functional in ... seconds. Tear down plone.testing.z2.FTPServer in ... seconds. - Tear down plone.testing.z2.FunctionalTesting in ... seconds. Tear down plone.testing.z2.Startup in ... seconds. Tear down plone.testing.zca.LayerCleanup in ... seconds. |