diff --git a/news/+enrich-api-docstrings.documentation b/news/+enrich-api-docstrings.documentation new file mode 100644 index 0000000..3e4c277 --- /dev/null +++ b/news/+enrich-api-docstrings.documentation @@ -0,0 +1,6 @@ +Enrich the docstrings of the public testing helpers and layer classes +(``login``, ``logout``, ``setRoles``, ``applyProfile``, +``cleanUpMultiPlugins``, ``IntegrationTesting``, ``FunctionalTesting``) +with parameter descriptions and behaviour notes. +This makes them usable as an autodoc API reference and improves IDE help. +[jensens] diff --git a/src/plone/app/testing/cleanup.py b/src/plone/app/testing/cleanup.py index e0d9957..f299345 100644 --- a/src/plone/app/testing/cleanup.py +++ b/src/plone/app/testing/cleanup.py @@ -16,6 +16,13 @@ def cleanUpMultiPlugins(): + """Remove PAS MultiPlugin registrations left in the global registry. + + Registered as a ``zope.testing`` cleanup handler, so it runs + automatically between test layers and keeps global PluggableAuthService + state from leaking from one layer into the next. Plugins that + GenericSetup's own cleanup handler already manages are left untouched. + """ try: from Products.PluggableAuthService.PluggableAuthService import MultiPlugins diff --git a/src/plone/app/testing/helpers.py b/src/plone/app/testing/helpers.py index 887cde5..e9d6f9b 100644 --- a/src/plone/app/testing/helpers.py +++ b/src/plone/app/testing/helpers.py @@ -22,19 +22,39 @@ def login(portal, userName): - """Log in as the given user in the given Plone site""" + """Log in as the named user in the given Plone site. + + Subsequent operations run as this user until :func:`logout` is called + or the test tears down. + + :param portal: the Plone site to log in to. + :param userName: the login name of the user to become, for example + :data:`TEST_USER_NAME`. This is the login name, not the user id. + """ zope.login(portal["acl_users"], userName) def logout(): - """Log out, i.e. become anonymous""" + """Log out, so that subsequent operations run as the anonymous user. + + The inverse of :func:`login`. + """ zope.logout() def setRoles(portal, userId, roles): - """Set the given user's roles to a tuple of roles.""" + """Set a user's global roles to exactly the given roles. + + Replaces the user's current roles rather than adding to them. + + :param portal: the Plone site. + :param userId: the id of the user whose roles to set, for example + :data:`TEST_USER_ID`. This is the user id, not the login name. + :param roles: the roles the user should have, as a sequence of role + names, for example ``["Manager"]``. + """ userFolder = portal["acl_users"] zope.setRoles(userFolder, userId, roles) @@ -105,8 +125,25 @@ def applyProfile( archive=None, blacklisted_steps=None, ): - """Install an extension profile into the portal. The profile name - should be a package name and a profile name, e.g. 'my.product:default'. + """Install a GenericSetup extension profile into the portal. + + Runs every import step of the profile as the site owner, then rebuilds + the current skin. Use this to install an add-on into the test site, + typically from a layer's ``setUpPloneSite`` or directly in a test. + + :param portal: the Plone site to install the profile into. + :param profileName: the profile id in ``package:profile`` form, for + example ``"my.product:default"``. Give it without the ``profile-`` + prefix; that is added internally. + :param purge_old: passed through to GenericSetup. When ``None`` (the + default) GenericSetup decides; ``True`` purges existing settings + before importing, ``False`` keeps them. + :param ignore_dependencies: when ``True``, run only the profile's own + import steps and skip the profiles it depends on. + :param archive: an optional GenericSetup archive (tarball) to import + from instead of the profile directory. + :param blacklisted_steps: an optional sequence of import step ids to + skip. """ from AccessControl import getSecurityManager diff --git a/src/plone/app/testing/layers.py b/src/plone/app/testing/layers.py index d61ff0f..a845561 100644 --- a/src/plone/app/testing/layers.py +++ b/src/plone/app/testing/layers.py @@ -398,11 +398,30 @@ def testTearDown(self): class IntegrationTesting(PloneTestLifecycle, zope.IntegrationTesting): - """Plone version of the integration testing layer""" + """Testing layer for integration tests. + + Wraps a Plone site fixture with per-test isolation: each test runs + inside a transaction that is rolled back on tear-down, so tests do not + see each other's changes. Derive one from your fixture with + ``IntegrationTesting(bases=(MY_FIXTURE,), name=...)``. + + Use this for in-process tests. When a test makes a real HTTP request, + use :class:`FunctionalTesting` instead. + """ class FunctionalTesting(PloneTestLifecycle, zope.FunctionalTesting): - """Plone version of the functional testing layer""" + """Testing layer for functional tests. + + Like :class:`IntegrationTesting`, but each test runs against a stacked + ``DemoStorage`` and may commit real transactions, which a separate + process such as a browser or HTTP client can then see. The storage is + discarded on tear-down. + + Use this when a request has to travel over the network, for example a + REST API or browser test. Add ``WSGI_SERVER_FIXTURE`` to the bases so a + server is running. For in-process tests, use :class:`IntegrationTesting`. + """ class ZServerFunctionalTesting(PloneZServerTestLifecycle, zserver.FunctionalTesting):