Zope Component Architecture layers
----------------------------------

The ZCA layers are found in the module ``plone.testing.zca``:

    >>> from plone.testing import zca

For testing, we need a testrunner

    >>> from zope.testing.testrunner import runner

Unit testing
~~~~~~~~~~~~

The ``UNIT_TESTING`` layer is used to set up a clean component registry
between each test. It uses ``zope.testing.cleanup`` to clean up all global
state.

It has no bases:

    >>> "%s.%s" % (zca.UNIT_TESTING.__module__, zca.UNIT_TESTING.__name__,)
    'plone.testing.zca.UnitTesting'

    >>> zca.UNIT_TESTING.__bases__
    ()

The component registry is cleaned up between each test.

    >>> from zope.interface import Interface
    >>> from zope.component import provideUtility
    
    >>> class DummyUtility(object):
    ...     def __init__(self, name):
    ...         self.name = name
    ...     def __repr__(self):
    ...         return "<%s>" % self.name

    >>> provideUtility(DummyUtility("Dummy"), provides=Interface, name="test-dummy")

    >>> from zope.component import queryUtility
    >>> queryUtility(Interface, name="test-dummy")
    <Dummy>

Layer setup does nothing.

    >>> options = runner.get_options([], [])
    >>> setupLayers = {}
    >>> runner.setup_layer(options, zca.UNIT_TESTING, setupLayers)
    Set up plone.testing.zca.UnitTesting in ... seconds.

Let's now simulate a test. Before any test setup has happened, our previously
registered utility is still there.

    >>> queryUtility(Interface, name="test-dummy")
    <Dummy>

On test setup, it disappears.

    >>> zca.UNIT_TESTING.testSetUp()

    >>> queryUtility(Interface, name="test-dummy") is None
    True

The test would now execute. It may register some components.

    >>> provideUtility(DummyUtility("Dummy2"), provides=Interface, name="test-dummy")
    >>> queryUtility(Interface, name="test-dummy")
    <Dummy2>

On test tear-down, this disappears.

    >>> zca.UNIT_TESTING.testTearDown()

    >>> queryUtility(Interface, name="test-dummy") is None
    True

Layer tear-down does nothing.

    >>> runner.tear_down_unneeded(options, [], setupLayers)
    Tear down plone.testing.zca.UnitTesting in ... seconds.

Event testing
~~~~~~~~~~~~~

The ``EVENT_TESTING`` layer extends the ``UNIT_TESTING`` layer to add the
necessary registrations for ``zope.component.eventtesting`` to work.

    >>> "%s.%s" % (zca.EVENT_TESTING.__module__, zca.EVENT_TESTING.__name__,)
    'plone.testing.zca.EventTesting'

    >>> zca.EVENT_TESTING.__bases__
    (<Layer 'plone.testing.zca.UnitTesting'>,)

Before the test, the component registry is empty and ``getEvents()`` returns
nothing, even if an event is fired.

    >>> from zope.component.eventtesting import getEvents

    >>> class DummyEvent(object):
    ...     def __repr__(self):
    ...         return "<Dummy event>"
    
    >>> from zope.event import notify
    >>> notify(DummyEvent())

    >>> getEvents()
    []

Layer setup does nothing.

    >>> options = runner.get_options([], [])
    >>> setupLayers = {}
    >>> runner.setup_layer(options, zca.EVENT_TESTING, setupLayers)
    Set up plone.testing.zca.UnitTesting in ... seconds.
    Set up plone.testing.zca.EventTesting in ... seconds.

Let's now simulate a test. On test setup, the event testing list is emptied.
    
    >>> zca.UNIT_TESTING.testSetUp()
    >>> zca.EVENT_TESTING.testSetUp()

    >>> getEvents()
    []

The test would now execute. It may fire some events, which would show up in
the event testing list.

    >>> notify(DummyEvent())
    >>> getEvents()
    [<Dummy event>]

On test tear-down, the list is emptied again

    >>> zca.EVENT_TESTING.testTearDown()
    >>> zca.UNIT_TESTING.testTearDown()

    >>> getEvents()
    []

Layer tear-down does nothing.

    >>> runner.tear_down_unneeded(options, [], setupLayers)
    Tear down plone.testing.zca.EventTesting in ... seconds.
    Tear down plone.testing.zca.UnitTesting in ... seconds.

Layer cleanup
~~~~~~~~~~~~~

The ``LAYER_CLEANUP`` layer is used to set up a clean component registry
at the set-up and tear-down of a layer. It uses ``zope.testing.cleanup`` to
clean up all global state.

It has no bases:

    >>> "%s.%s" % (zca.LAYER_CLEANUP.__module__, zca.LAYER_CLEANUP.__name__,)
    'plone.testing.zca.LayerCleanup'

    >>> zca.LAYER_CLEANUP.__bases__
    ()

The component registry is cleaned up on layer set-up and tear-down (but not
between tests).

    >>> from zope.interface import Interface
    >>> from zope.component import provideUtility
    
    >>> class DummyUtility(object):
    ...     def __init__(self, name):
    ...         self.name = name
    ...     def __repr__(self):
    ...         return "<%s>" % self.name

    >>> provideUtility(DummyUtility("Dummy"), provides=Interface, name="test-dummy")

    >>> from zope.component import queryUtility
    >>> queryUtility(Interface, name="test-dummy")
    <Dummy>

    >>> options = runner.get_options([], [])
    >>> setupLayers = {}
    >>> runner.setup_layer(options, zca.LAYER_CLEANUP, setupLayers)
    Set up plone.testing.zca.LayerCleanup in ... seconds.

    >>> queryUtility(Interface, name="test-dummy") is None
    True

A sub-layer may register additional components:

    >>> provideUtility(DummyUtility("Dummy2"), provides=Interface, name="test-dummy2")

Let's now simulate a test. Test setup and tear-down does nothing.

    >>> zca.LAYER_CLEANUP.testSetUp()

    >>> queryUtility(Interface, name="test-dummy") is None
    True
    >>> queryUtility(Interface, name="test-dummy2")
    <Dummy2>

    >>> zca.LAYER_CLEANUP.testTearDown()

    >>> queryUtility(Interface, name="test-dummy") is None
    True
    >>> queryUtility(Interface, name="test-dummy2")
    <Dummy2>

On tear-down, the registry is cleaned again.

    >>> runner.tear_down_unneeded(options, [], setupLayers)
    Tear down plone.testing.zca.LayerCleanup in ... seconds.

    >>> queryUtility(Interface, name="test-dummy") is None
    True
    >>> queryUtility(Interface, name="test-dummy2") is None
    True

Basic ZCML directives
~~~~~~~~~~~~~~~~~~~~~

The ``ZCML_DIRECTIVES`` layer creates a ZCML configuration context with the
basic ``zope.component`` directives available. It extends the
``LAYER_CLEANUP`` layer.

    >>> "%s.%s" % (zca.ZCML_DIRECTIVES.__module__, zca.ZCML_DIRECTIVES.__name__,)
    'plone.testing.zca.ZCMLDirectives'

    >>> zca.ZCML_DIRECTIVES.__bases__
    (<Layer 'plone.testing.zca.LayerCleanup'>,)

Before the test, we cannot use e.g. a ``<utility />`` directive without
loading the necessary ``meta.zcml`` files.

    >>> from zope.configuration import xmlconfig
    >>> xmlconfig.string("""\
    ... <configure package="plone.testing" xmlns="http://namespaces.zope.org/zope">
    ...     <utility factory=".tests.DummyUtility" provides="zope.interface.Interface" name="test-dummy" />
    ... </configure>""")
    Traceback (most recent call last):
    ...
    ZopeXMLConfigurationError: File "<string>", line 2.4
        ConfigurationError: ('Unknown directive', u'http://namespaces.zope.org/zope', u'utility')

Layer setup creates a configuration context we can use to load further
configuration.

    >>> options = runner.get_options([], [])
    >>> setupLayers = {}
    >>> runner.setup_layer(options, zca.ZCML_DIRECTIVES, setupLayers)
    Set up plone.testing.zca.LayerCleanup in ... seconds.
    Set up plone.testing.zca.ZCMLDirectives in ... seconds.

Let's now simulate a test that uses this configuration context to load the
same ZCML string.
    
    >>> zca.ZCML_DIRECTIVES.testSetUp()

    >>> context = zca.ZCML_DIRECTIVES['configurationContext'] # would normally be self.layer['configurationContext']
    >>> xmlconfig.string("""\
    ... <configure package="plone.testing" xmlns="http://namespaces.zope.org/zope">
    ...     <utility factory=".tests.DummyUtility" provides="zope.interface.Interface" name="test-dummy" />
    ... </configure>""", context=context) is context
    True
    
The utility is now registered:

    >>> queryUtility(Interface, name="test-dummy")
    <Dummy utility>

    >>> zca.UNIT_TESTING.testTearDown()

Note that normally, we'd combine this with the ``UNIT_TESTING`` layer to tear
down the component architecture as well.

Layer tear-down deletes the configuration context.

    >>> runner.tear_down_unneeded(options, [], setupLayers)
    Tear down plone.testing.zca.ZCMLDirectives in ... seconds.

    >>> zca.ZCML_DIRECTIVES.get('configurationContext', None) is None
    True

Configuration registry sandboxing
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

For simple unit tests, the full cleanup performed between each test using the
``UNIT_TESTING`` layer is undoubtedly the safest and most convenient way to
ensure proper isolation of tests using the global component architecture.
However, if you are writing a complex layer that sets up a lot of components,
you may wish to keep some components registered at the layer level, whilst
still allowing tests and sub-layers to register their own components in
isolation.

This is a tricky problem, because the default ZCML directives and APIs
(``provideAdapter()``, ``provideUtility()`` and so on) explicitly work on
a single global adapter registry object. To get around this, you can use two
helper methods in the ``zca`` module to push a new global component registry
before registering components, and pop the registry after. Registries are
stacked, so the components registered in a "lower" registry are automatically
available in a "higher" registry.

Let's illustrate this with a layer that stacks two new global registries. The
first registry is specific to the layer, and is used to house the components
registered at the layer level. The second registry is set up and torn down for
each test, allowing tests to register their own components freely.

First, we'll create a simple dummy utility to illustrate registrations.

    >>> from zope.interface import Interface, implements

    >>> class IDummyUtility(Interface):
    ...     pass
    >>> class DummyUtility(object):
    ...     implements(IDummyUtility)
    ...     def __init__(self, name):
    ...         self.name = name
    ...     def __repr__(self):
    ...         return "<DummyUtility %s>" % self.name

The two key methods are:

* ``zca.pushGlobalRegistry()``, which creates a new global registry.
* ``zca.popGlobalRegistry()``, which restores the previous global registry.

  **Warning:** You *must* balance your calls to these methods. If you call
  ``pushGlobalRegistry()`` in ``setUp()``, call ``popGlobalRegistry()`` in
  ``tearDown()``. Ditto for ``testSetUp()`` and ``testTearDown()``.

Let's now create our layer.

    >>> from zope.component import provideUtility
    >>> from plone.testing import Layer
    >>> from plone.testing import zca

    >>> class ComponentSandbox(Layer):
    ...     def setUp(self):
    ...         zca.pushGlobalRegistry()
    ...         provideUtility(DummyUtility("layer"), name="layer")
    ...     def tearDown(self):
    ...         zca.popGlobalRegistry()
    ...     def testSetUp(self):
    ...         zca.pushGlobalRegistry()
    ...     def testTearDown(self):
    ...         zca.popGlobalRegistry()
    >>> COMPONENT_SANDBOX = ComponentSandbox()

Let's now simulate a test using this layer.

To begin with, we have the default registry.

    >>> from zope.component import getGlobalSiteManager, getSiteManager
    >>> getSiteManager() is getGlobalSiteManager()
    True
    
    >>> defaultGlobalSiteManager = getGlobalSiteManager()
    
    >>> from zope.component import queryUtility
    >>> queryUtility(IDummyUtility, name="layer") is None
    True

We'll now simulate layer setup. This will push a new registry onto the stack:

    >>> COMPONENT_SANDBOX.setUp()

    >>> getSiteManager() is getGlobalSiteManager()
    True
    >>> getGlobalSiteManager() is defaultGlobalSiteManager
    False
    >>> layerGlobalSiteManager = getGlobalSiteManager()
    
    >>> queryUtility(IDummyUtility, name="layer")
    <DummyUtility layer>

We'll then simulate a test that registers a global component:

    >>> COMPONENT_SANDBOX.testSetUp()

    >>> getSiteManager() is getGlobalSiteManager()
    True
    >>> getGlobalSiteManager() is defaultGlobalSiteManager
    False
    >>> getGlobalSiteManager() is layerGlobalSiteManager
    False

Our previously registered component is still here.

    >>> queryUtility(IDummyUtility, name="layer")
    <DummyUtility layer>

We can also register a new one.

    >>> provideUtility(DummyUtility("test"), name="test")
    >>> queryUtility(IDummyUtility, name="layer")
    <DummyUtility layer>
    >>> queryUtility(IDummyUtility, name="test")
    <DummyUtility test>

On test tear-down, only the second utility disappears:

    >>> COMPONENT_SANDBOX.testTearDown()

    >>> getSiteManager() is getGlobalSiteManager()
    True
    >>> getGlobalSiteManager() is defaultGlobalSiteManager
    False
    >>> getGlobalSiteManager() is layerGlobalSiteManager
    True

    >>> queryUtility(IDummyUtility, name="layer")
    <DummyUtility layer>
    >>> queryUtility(IDummyUtility, name="test") is None
    True
    
If we tear down the layer too, we're back where we started:

    >>> COMPONENT_SANDBOX.tearDown()

    >>> getSiteManager() is getGlobalSiteManager()
    True
    >>> getGlobalSiteManager() is defaultGlobalSiteManager
    True

    >>> queryUtility(IDummyUtility, name="layer") is None
    True
    >>> queryUtility(IDummyUtility, name="test") is None
    True
