Part 4: Testing Views¶
Test Simple View¶
class TaskViewIntegrationTest(unittest.TestCase):
layer = PLONETRAINING_TESTING_INTEGRATION_TESTING
def setUp(self):
self.portal = self.layer['portal']
self.request = self.layer['request']
setRoles(self.portal, TEST_USER_ID, ['Manager'])
self.portal.invokeFactory('Task', id='task', title='Task')
self.task = self.portal.task
def test_view_with_get_multi_adapter(self):
# Get the view
view = getMultiAdapter((self.task, self.request), name="view")
# Put the view into the acquisition chain
view = view.__of__(self.task)
# Call the view
self.assertTrue(view())
def test_view_with_restricted_traverse(self):
view = self.task.restrictedTraverse('view')
self.assertTrue(view())
def test_view_with_unrestricted_traverse(self):
view = self.task.unrestrictedTraverse('view')
self.assertTrue(view())
def test_view_html_structure(self):
import lxml
view = getMultiAdapter((self.task, self.request), name="view")
view = view.__of__(self.task)
output = lxml.html.fromstring(view())
self.assertEqual(1, len(output.xpath("/html/body/div")))
Implementation¶
browser/configure.zcml:
<!-- Task View -->
<browser:page
name="view"
for="plonetraining.testing.interfaces.ITask"
class=".task.TaskView"
permission="zope2.View"
/>
browser/task.py:
from zope.site.hooks import getSite
from Products.Five.browser import BrowserView
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
import json
class TaskView(BrowserView):
template = ViewPageTemplateFile('task.pt')
browser/task.pt:
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal"
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
lang="en"
metal:use-macro="here/prefs_main_template/macros/master"
i18n:domain="plonetraining.testing">
<body>
<div metal:fill-slot="content-core">
<h1 tal:content="context/title">Task</h1>
<p tal:content="context/description|nothing">Task Description</p>
<p>
Request Term:
<span tal:content="context/term|nothing">Request Term</span>
</p>
</div>
</body>
</html>
Test View Browserlayer¶
class TaskViewWithBrowserlayerIntegrationTest(unittest.TestCase):
layer = PLONETRAINING_TESTING_INTEGRATION_TESTING
def setUp(self):
self.portal = self.layer['portal']
self.request = self.layer['request']
setRoles(self.portal, TEST_USER_ID, ['Manager'])
self.portal.invokeFactory('Task', id='task', title='Task')
self.task = self.portal.task
def test_view_with_browserlayer(self):
# Make the request provide the browser layer so our view can be looked
# up
from zope.interface import directlyProvides
from plonetraining.testing.interfaces import IPlonetrainingTestingLayer
directlyProvides(self.request, IPlonetrainingTestingLayer)
# Get the view
view = getMultiAdapter(
(self.task, self.request),
name="view-with-browserlayer"
)
# Put the view into the acquisition chain
view = view.__of__(self.task)
# Call the view
self.assertTrue(view())
Test View with Request Parameter¶
class TaskViewWithRequestParameterIntegrationTest(unittest.TestCase):
layer = PLONETRAINING_TESTING_INTEGRATION_TESTING
def setUp(self):
self.portal = self.layer['portal']
self.request = self.layer['request']
setRoles(self.portal, TEST_USER_ID, ['Manager'])
self.portal.invokeFactory('Task', id='task', title='Task')
self.task = self.portal.task
def test_view_with_request_parameter(self):
self.request.set('term', 'foo')
view = getMultiAdapter(
(self.task, self.request),
name="view-with-params"
)
view = view.__of__(self.task)
self.failUnless(view())
Test Protected View¶
class TaskViewProtectedIntegrationTest(unittest.TestCase):
layer = PLONETRAINING_TESTING_INTEGRATION_TESTING
def setUp(self):
self.portal = self.layer['portal']
self.request = self.layer['request']
setRoles(self.portal, TEST_USER_ID, ['Manager'])
self.portal.invokeFactory('Task', id='task', title='Task')
self.task = self.portal.task
def test_view_protected(self):
"""Try to access a protected view and make sure we raise Unauthorized.
"""
from AccessControl import Unauthorized
logout()
self.assertRaises(
Unauthorized,
self.task.restrictedTraverse,
'view-protected'
)
Test JSON View¶
class TaskViewJsonIntegrationTest(unittest.TestCase):
layer = PLONETRAINING_TESTING_INTEGRATION_TESTING
def setUp(self):
self.portal = self.layer['portal']
self.request = self.layer['request']
setRoles(self.portal, TEST_USER_ID, ['Manager'])
self.portal.invokeFactory('Task', id='task', title='Task')
self.task = self.portal.task
def test_view_json(self):
view = getMultiAdapter(
(self.task, self.request),
name="view-json"
)
view = view.__of__(self.task)
self.assertEqual(
{
u'title': u'Task',
u'description': u''
},
json.loads(view())
)
self.assertEqual(
'application/json; charset=utf-8',
view.request.response.headers.get('content-type'),
)
Test XML View¶
class TaskViewXmlIntegrationTest(unittest.TestCase):
layer = PLONETRAINING_TESTING_INTEGRATION_TESTING
def setUp(self):
self.portal = self.layer['portal']
self.request = self.layer['request']
setRoles(self.portal, TEST_USER_ID, ['Manager'])
self.portal.invokeFactory('Task', id='task', title='Task')
self.task = self.portal.task
def test_view_json(self):
view = getMultiAdapter(
(self.task, self.request),
name="view-xml"
)
view = view.__of__(self.task)
import lxml
output = lxml.etree.fromstring(view())
self.assertEqual(len(output.xpath("/task/title")), 1)
self.assertEqual(output.xpath("/task/title")[0].text, u'Task')
self.assertEqual(len(output.xpath("/task/description")), 1)
self.assertEqual(output.xpath("/task/description")[0].text, None)
self.assertEqual(
'application/xml; charset=utf-8',
view.request.response.headers.get('content-type')
)
Test View Redirect¶
class TaskViewRedirectIntegrationTest(unittest.TestCase):
layer = PLONETRAINING_TESTING_INTEGRATION_TESTING
def setUp(self):
self.portal = self.layer['portal']
self.request = self.layer['request']
setRoles(self.portal, TEST_USER_ID, ['Manager'])
self.portal.invokeFactory('Task', id='task', title='Task')
self.task = self.portal.task
def test_view_redirect(self):
view = getMultiAdapter(
(self.task, self.request),
name="view-redirect"
)
view = view.__of__(self.task)
view()
self.assertEqual(
'http://nohost/plone',
self.request.response.headers['location']
)
Troubleshooting¶
KeyError: ‘ACTUAL_URL’¶
Sometimes a view expect an ‘ACTUAL_URL’ param. If this is the case, make sure you provide the param in the test request:
def setUp(self):
self.portal = self.layer['portal']
self.request = self.layer['request']
setRoles(self.portal, TEST_USER_ID, ['Manager'])
self.portal.invokeFactory('Folder', 'test-folder')
self.folder = self.portal['test-folder']
self.request.set('URL', self.folder.absolute_url())
self.request.set('ACTUAL_URL', self.folder.absolute_url())
def test_view(self):
view = self.collection.restrictedTraverse('@@RSS')
self.assertTrue(view())
self.assertEquals(view.request.response.status, 200)
ComponentLookupError¶
If a view can not be looked up on a particular context, Plone will raise a ComponentLookupError (because views are multi-adapters), e.g.:
ComponentLookupError: ((<PloneSite at /plone>, <HTTPRequest, URL=http://nohost/plone>), <InterfaceClass zope.interface.Interface>, 'recipes')::
This can be solved for instance by providing a browser layer that has been missing:
def setUp(self):
self.request = self.layer['request']
from zope.interface import directlyProvides
directlyProvides(self.request, IMyCompanyContenttypes)
...