summaryrefslogtreecommitdiff
path: root/repoze/bfg/testing.py
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2008-11-07 10:47:28 +0000
committerChris McDonough <chrism@agendaless.com>2008-11-07 10:47:28 +0000
commitdeb0dc316b64d5fb7bd0e15a1bafe269d3b33fbc (patch)
tree669d3eb3f6d00ce866b1d48fc977ca372aebc396 /repoze/bfg/testing.py
parent569ba5128fb1fd028e0fe879e810f855a61c8a03 (diff)
downloadpyramid-deb0dc316b64d5fb7bd0e15a1bafe269d3b33fbc.tar.gz
pyramid-deb0dc316b64d5fb7bd0e15a1bafe269d3b33fbc.tar.bz2
pyramid-deb0dc316b64d5fb7bd0e15a1bafe269d3b33fbc.zip
Features
- Added a ``repoze.bfg.testing`` module to attempt to make it slightly easier to write unittest-based automated tests of BFG applications. Information about this class is in the documentation. - The default template renderer now supports testing better by looking for ``ITestingTemplateRenderer`` using a relative pathname. This is exposed indirectly through the API named ``registerTemplate`` in ``repoze.bfg.testing``. Deprecations - The names ``repoze.bfg.interfaces.ITemplate`` , ``repoze.bfg.interfaces.ITemplateFactory`` and ``repoze.bfg.interfaces.INodeTemplate`` have been deprecated. These should now be imported as ``repoze.bfg.interfaces.ITemplateRenderer`` and ``repoze.bfg.interfaces.ITemplateRendererFactory``, and ``INodeTemplateRenderer`` respectively. - The name ``repoze.bfg.chameleon_zpt.ZPTTemplateFactory`` is deprecated. Use ``repoze.bfg.chameleon_zpt.ZPTTemplateRenderer``. - The name ``repoze.bfg.chameleon_genshi.GenshiTemplateFactory`` is deprecated. Use ``repoze.bfg.chameleon_genshi.GenshiTemplateRenderer``. - The name ``repoze.bfg.xslt.XSLTemplateFactory`` is deprecated. Use ``repoze.bfg.xslt.XSLTemplateRenderer``.
Diffstat (limited to 'repoze/bfg/testing.py')
-rw-r--r--repoze/bfg/testing.py275
1 files changed, 275 insertions, 0 deletions
diff --git a/repoze/bfg/testing.py b/repoze/bfg/testing.py
new file mode 100644
index 000000000..2ada3d20e
--- /dev/null
+++ b/repoze/bfg/testing.py
@@ -0,0 +1,275 @@
+import unittest
+from zope.component.testing import PlacelessSetup
+from zope.interface import Interface
+
+class BFGTestCase(unittest.TestCase, PlacelessSetup):
+ """ A class which inherits from both ``unittest.TestCase`` and
+ ``zope.component.testing.PlacelessSetup`` that provides a
+ convenience API for writing BFG-specific tests. This class can be
+ subclassed within test modules and those subclasses will be found
+ by test loaders. Since this test case inherits from
+ ``PlacelessSetup`` the Zope component architecture registry is set
+ up and torn down between each test, which provides isolation
+ between tests."""
+
+ def setUp(self):
+ PlacelessSetup.setUp(self)
+
+ def tearDown(self):
+ PlacelessSetup.tearDown(self)
+
+ def registerSecurityPolicy(self, userid=None, groupids=(), permissive=True):
+ """ Registers a ``repoze.bfg`` security policy using the
+ userid ``userid`` and the group ids ``groupids``. If
+ ``permissive`` is true, a 'permissive' security policy is
+ registered; this policy allows all access. If ``permissive``
+ is false, a nonpermissive security policy is registered; this
+ policy denies all access. To register your own (possibly more
+ granular) security policy, see the ``registerSecurityPolicy``
+ *function* in the testing package. This function is most
+ useful when dealing with code that uses the
+ ``repoze.bfg.security``APIs named ``has_permission``,
+ ``authenticated_userid``, effective_principals, and
+ ``principals_allowed_by_permission``."""
+ if permissive:
+ policy = DummyAllowingSecurityPolicy(userid, groupids)
+ else:
+ policy = DummyDenyingSecurityPolicy(userid, groupids)
+ return registerSecurityPolicy(policy)
+
+ def registerModels(self, models):
+ """ Registers a dictionary of models. This is most useful for
+ dealing with code that wants to call the
+ ``repoze.bfg.traversal.find_model`` API. This API is called
+ with a path as one of its arguments. If the dictionary you
+ register when calling this method contains that path as a key
+ (e.g. '/foo/bar' or 'foo'), the corresponding value will be
+ returned to ``find_model`` (and thus to your code)."""
+ traverser = make_traverser_factory(models)
+ registerTraverserFactory(traverser)
+ return models
+
+ def registerTemplate(self, name):
+ """ Registers a 'dummy' template renderer implementation and
+ returns it. This is most useful when dealing with code that
+ wants to call ``repoze.bfg.chameleon_zpt.render_template*``or
+ ``repoze.bfg.chameleon_genshi.render_template*``. If you call
+ this method with the exact template path string that a call to
+ one of the ``render_template`` functions uses, the dummy
+ template will stand in for the real implementation. The dummy
+ template object will set attributes on itself corresponding to
+ the non-path keyword arguments provided to the ``render``
+ function. You can then compare these values against what you
+ expect. """
+ return registerTemplateRenderer(name)
+
+ def registerEventListener(self, event_iface=Interface):
+ """ Registers an event listener (aka 'subscriber') listening
+ for events of the type ``event_iface`` and returns a list
+ which is appended to by the subscriber. When an event is
+ dispatched that matches ``event_iface``, that event will be
+ appended to the list. You can then compare the values in the
+ list to expected event notifications. This method is useful
+ when dealing with code that wants to call
+ ``zope.component.event.dispatch``."""
+ L = []
+ def subscriber(event):
+ L.append(event)
+ registerSubscriber(subscriber, event_iface)
+ return L
+
+ def registerView(self, name, result='', view=None):
+ """ Registers a ``repoze.bfg`` view function under the name
+ ``name``. The view will return a webob Response object with
+ the ``result`` value as its body attribute. To gain more
+ control, if you pass in a non-None ``view``, this view
+ function will be used instead of an automatically generated
+ view function (and ``result`` is not used). This method is
+ useful when dealing with code that wants to call,
+ e.g. ``repoze.bfg.view.render_view_to_response``."""
+ if view is None:
+ view = make_view(result)
+ registerView(view, name)
+ return view
+
+ def registerViewPermission(self, name, result=True, viewpermission=None):
+ """ Registers a ``repoze.bfg`` 'view permission' object under
+ the name ``name``. The view permission return a result
+ denoted by the ``result`` argument. If ``result`` is True, a
+ ``repoze.bfg.security.Allowed`` object is returned; else a
+ ``repoze.bfg.security.Denied`` object is returned. To gain
+ more control, if you pass in a non-None ``viewpermission``,
+ this view permission object will be used instead of an
+ automatically generated view permission (and ``result`` is not
+ used). This method is useful when dealing with code that
+ wants to call, e.g. ``repoze.bfg.view.view_execution_permitted``.
+ Note that view permissions are not checked unless a security
+ policy is in effect (see ``registerSecurityPolicy``)."""
+ from repoze.bfg.security import Allowed
+ from repoze.bfg.security import Denied
+ if result is True:
+ result = Allowed('message')
+ else:
+ result = Denied('message')
+ if viewpermission is None:
+ viewpermission = make_view_permission(result)
+ return registerViewPermission(viewpermission, name)
+
+ def registerAdapter(self, impl, for_, provides, name=''):
+ """ A shortcut for calling the Zope Component Architecture's
+ global site manager's ``registerAdapter`` function. The
+ argument ordering matches that function's exactly. Registers a
+ ZCA adapter."""
+ return registerAdapter(impl, for_, provides, name)
+
+ def registerUtility(self, impl, iface, name=''):
+ """ A shortcut for calling the Zope Component Architecture's
+ global site manager's ``registerUtility`` function. The
+ argument ordering matches that function's exactly. Registers
+ a ZCA utility."""
+ return registerUtility(impl, iface, name)
+
+ def makeModel(self, name=None, parent=None):
+ """ Returns a 'dummy' model object, with the model's
+ ``__name__`` attribute set to the value of ``name``, and the
+ model's ``__parent__`` attribute set to the value of
+ ``parent``. A dummy model has a ``__setitem__`` method and a
+ ``__getitem__`` method. The ``__setitem__`` method can be
+ called with a key/value pair; the value will be decorated with
+ a ``__parent__`` attribute pointing at the dummy object and a
+ ``__name__`` attribute that is the value of the key.
+ A dummy model has no other attributes or methods."""
+ return DummyModel(name, parent)
+
+def registerUtility(impl, iface, name=''):
+ import zope.component
+ gsm = zope.component.getGlobalSiteManager()
+ gsm.registerUtility(impl, iface, name=name)
+ return impl
+
+def registerAdapter(impl, for_=Interface, provides=Interface, name=''):
+ import zope.component
+ gsm = zope.component.getGlobalSiteManager()
+ if not isinstance(for_, (tuple, list)):
+ for_ = (for_,)
+ gsm.registerAdapter(impl, for_, provides, name=name)
+ return impl
+
+def registerSubscriber(subscriber, iface=Interface):
+ import zope.component
+ gsm = zope.component.getGlobalSiteManager()
+ if not isinstance(iface, (tuple, list)):
+ iface = (iface,)
+ gsm.registerHandler(subscriber, iface)
+ return subscriber
+
+def registerTemplateRenderer(path, renderer=None, iface=None):
+ if iface is None:
+ from repoze.bfg.interfaces import ITestingTemplateRenderer
+ iface = ITestingTemplateRenderer
+ if renderer is None:
+ renderer = DummyTemplateRenderer()
+ return registerUtility(renderer, iface, path)
+
+def registerSecurityPolicy(policy):
+ from repoze.bfg.interfaces import ISecurityPolicy
+ return registerUtility(policy, ISecurityPolicy)
+
+def registerTraverserFactory(traverser, for_=Interface):
+ from repoze.bfg.interfaces import ITraverserFactory
+ return registerAdapter(traverser, for_, ITraverserFactory)
+
+def registerView(view, name, for_=(Interface, Interface)):
+ from repoze.bfg.interfaces import IView
+ return registerAdapter(view, for_, IView, name)
+
+def registerViewPermission(viewpermission, name, for_=(Interface, Interface)):
+ from repoze.bfg.interfaces import IViewPermission
+ return registerAdapter(viewpermission, for_, IViewPermission, name)
+
+class _DummySecurityPolicy:
+ def __init__(self, userid=None, groupids=()):
+ self.userid = userid
+ self.groupids = groupids
+
+ def authenticated_userid(self, request):
+ return self.userid
+
+ def effective_principals(self, request):
+ from repoze.bfg.security import Everyone
+ from repoze.bfg.security import Authenticated
+ effective_principals = [Everyone]
+ if self.userid:
+ effective_principals.append(Authenticated)
+ effective_principals.append(self.userid)
+ effective_principals.extend(self.groupids)
+ return effective_principals
+
+class DummyAllowingSecurityPolicy(_DummySecurityPolicy):
+ def permits(self, context, request, permission):
+ return True
+
+ def principals_allowed_by_permission(self, context, permission):
+ return self.effective_principals(None)
+
+class DummyDenyingSecurityPolicy(_DummySecurityPolicy):
+ def permits(self, context, request, permission):
+ return False
+
+ def principals_allowed_by_permission(self, context, permission):
+ return []
+
+def make_traverser_factory(root):
+ class DummyTraverserFactory:
+ def __init__(self, context):
+ self.context = context
+
+ def __call__(self, environ):
+ ob = root[environ['PATH_INFO']]
+ return ob, '', []
+
+ return DummyTraverserFactory
+
+class DummyTemplateRenderer:
+ def implementation(self):
+ return None
+
+ def __call__(self, **kw):
+ self.__dict__.update(kw)
+ return ''
+
+def make_view(result):
+ def dummy_view(context, request):
+ from webob import Response
+ return Response(result)
+ return dummy_view
+
+def make_view_permission(result):
+ class DummyViewPermission:
+ def __init__(self, context, request):
+ self.context = context
+ self.request = request
+
+ def __call__(self, secpol):
+ return result
+
+ return DummyViewPermission
+
+class DummyModel:
+ def __init__(self, name=None, parent=None):
+ self.__name__ = name
+ self.__parent__ = parent
+ self.subs = {}
+
+ def __setitem__(self, name, val):
+ val.__name__ = name
+ val.__parent__ = self
+ self.subs[name] = val
+
+ def __getitem__(self, name):
+ ob = self.subs[name]
+ return ob
+
+
+
+