Testing for fun and profit – Part 2
April, 27th 2010
Timo Stollenwerk
Part 1 – Why testing matters
● Reduces defects● Find bugs early● Proof that you delivered what you were
contracted to produce● Refactor with confidence● Lots of small successes
Testing pyramid
What are you going to learn today?
● Set up integration and functional tests● Traditional test setup● Test setup with layers
● Testing● Policy product● Dexterity types● Archetypes● Portlets
The current state of testing in Plone
● Plone 3 / PPD / Paster● collective.testcaselayer● plone.testing
Plone 3 test structure
$ paster create t archetype upc.myarchetype
● tests● __init__.py● base.py● test_doctest.py
Test setup: base.py
● Import ZopeTestCase and PloneTestCase● setup_product function● setupPloneSite● TestCase & FunctionalTestCase
Test setup: base.py (import)
from Testing import ZopeTestCase as ztc
from Products.PloneTestCase import PloneTestCase as ptc
from Products.PloneTestCase.layer import onsetup
Test setup: base.py (setup_product)
@onsetup
def setup_product():
fiveconfigure.debug_mode = True
import upc.myarchetype
zcml.load_config('configure.zcml', upcexample.myarchetype)
fiveconfigure.debug_mode = False
ztc.installPackage('upcexample.theme')
Test setup: base.py (test classes)
class TestCase(ptc.PloneTestCase):
"""We use this base class for all the tests in this package. If
necessary, we can put common utility or setup code in here. This
applies to unit test cases.
"""
class FunctionalTestCase(ptc.FunctionalTestCase):
"""We use this class for functional integration tests that use
doctest syntax. Again, we can put basic common utility or setup
code in here.
""”
TestCasefrom upcexample.myproduct.tests.base import TestCase
class TestSetup(TestCase):
def afterSetUp(self):
self.workflow = getToolByName(self.portal, 'portal_workflow')
def test_portal_title(self):
self.assertEquals("UPC Portal", self.portal.getProperty('title'))
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestSetup))
return suite
FunctionalTestCaseimport unittest
import doctest
from Testing import ZopeTestCase as ztc
from upctest.myproduct.tests import base
def test_suite():
return unittest.TestSuite([
# Demonstrate the main content types
ztc.ZopeDocFileSuite(
'README.txt', package=upctest.myproduct',
test_class=base.FunctionalTestCase,
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE |
doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS),
])
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')
FunctionalTestCase Doctest
Being a doctest, we can tell a story here.
>>> from Products.Five.testbrowser import Browser
>>> browser = Browser()
>>> portal_url = self.portal.absolute_url()
FunctionalTestCase: Login
>>> from Products.PloneTestCase.setup import portal_owner, default_password
>>> browser.open(portal_url)
We have the login portlet, so let's use that.
>>> browser.getControl(name='__ac_name').value = portal_owner
>>> browser.getControl(name='__ac_password').value = default_password
>>> browser.getControl(name='submit').click()
FunctionalTestCase: Properties
>>> browser.url == portal_url
True
>>> "You are now logged in" in browser.contents
True
>>> 'foo' in browser.headers
False
FunctionalTestCase: Actions
browser.open(url)
browser.reload()
browser.goBack(count=1)
browser.getLink(text=None, url=None, id=None)
browser.click()
browser.getControl(label=None, name=None, index=None)
Zope Testrunner (Plone 3)
● Test package
$ ./bin/instance test s upcexample.myproduct
Zope Testrunner (Plone 3)
● Test single file
$ ./bin/instance test s upcexample.myproduct t test_setup
Zope Testrunner (Plone 3)
● Unit/Integration tests only
$ ./bin/instance test s upcexample.myproduct u
Zope Testrunner (Plone 3)
● Debug
$ ./bin/instance test D
Setting Up Tests with Layers
● Tests (upc.testingtutorial / example.conference)● __init__.py● layer.py● test_functional_doctest.py● functional.txt● test_task.py
Setting Up Tests with Layers: layer.py
# Imports
ptc.setupPloneSite(
extension_profiles=('upc.testingtutorial:default', )
)
class IntegrationTestLayer(collective.testcaselayer.ptc.BasePTCLayer):
def afterSetUp(self):
self.addProfile('upc.testingtutorial:default')
Layer = IntegrationTestLayer([collective.testcaselayer.ptc.ptc_layer])
Setting Up Tests with Layers: test_functional_doctest.py
# Imports
from upc.testingtutorial.tests.layer import Layer
def test_suite():
return unittest.TestSuite([
ztc.ZopeDocFileSuite(
'functional.txt', package='upc.testingtutorial.tests',
test_class=ptc.FunctionalTestCase,
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE | doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS),
])
Setting Up Tests with Layers: functional.txt
==========================================
UPC Testing Tutorial: Functional Doctest
==========================================
This package contains a functional doctest for the task content type of the
upc.testringtutorial package. In this testbrowser doctest, we will demonstrate
how the task content type works. See tests/test_functional_doctest.py for how
it is set up.
>>> from Products.Five.testbrowser import Browser
>>> browser = Browser()
>>> portal_url = self.portal.absolute_url()
Setting Up Tests with Layers: test_task.py
class TestTaskIntegration(PloneTestCase):
layer = Layer
def test_adding(self):
self.folder.invokeFactory('upc.testingtutorial.task', 'task1')
t1 = self.folder['task1']
self.failUnless(ITask.providedBy(t1))
def test_suite():
return unittest.defaultTestLoader.loadTestsFromName(__name__)
Zope Testrunner (Plone 4)
[buildout]
parts =
...
test
…
[test]
recipe = zc.recipe.testrunner
eggs = upcexample.myproduct
defaults = ['v', 'exitwithstatus', 'autocolor', 'autoprogress']
Zope Testrunner (Plone 4)
● Run all tests
$ ./bin/test● Test one package
$ ./bin/test s upcexample.mypackage
...
Testing Portlets
class TestPortlet(TestCase):
def afterSetUp(self):
self.setRoles(('Manager',))
…
class TestRenderer(TestCase):
def afterSetUp(self):
self.setRoles(('Manager',))
self.portal.invokeFactory('Folder', 'cf1')
....
http://svn.plone.org/svn/collective/collective.portlet.recentactivity/trunk/collective/portlet/recentactivity/tests/test_portlet_recent_activity.py
Testing Viewlets
class TestCommentsViewletIntegration(FunctionalTestCase):
layer = DiscussionLayer
def testCommentsViewlet(self):
browser = Browser()
portal_url = self.portal.absolute_url()
browser.handleErrors = False
from Products.PloneTestCase.setup import portal_owner, default_password
browser.open(portal_url + '/login_form')
http://svn.plone.org/svn/plone/plone.app.discussion/trunk/plone/app/discussion/tests/test_comments_viewlet.py
Summary
● Setting up tests● Traditional way (PPD / Optilux / ZopeSkel)
● Testcaselayer (Dexterity manual)
● Future: plone.testing
● Archetypes● PPD / Optilux / ZopeSkel
● Dexterity● Dexterity manual (example.conference)
● Portlet● ZopeSkel / Paster output
● Viewlet● plone.app.discussion / plone.app.layout (search for *test*.py)
Testing Resources
● Plone Testing Tutorial● http://plone.org/documentation/kb/testing
● Plone 3 / Archetypes ● PPD / Optilux
● Plone 4 / Dexterity Manual / example.conference● http://plone.org/products/dexterity/documentation/manual/developermanual● http://svn.plone.org/svn/collective/example.conference/
The End
Top Related