plone testingdzug tagung2010
TRANSCRIPT
Plone Testing
DZUG Tagung Dresden 2010
Timo Stollenwerk
Timo Stollenwerk
● Plone Entwickler seit 2004● GSoC 2009: plone.app.discussion
● ~ 120 Tests● Python und Javascript● Code Analysis (Pylint, ZPTLint, JSLint)● Code Coverage● Continuous Integration
Material
● Folien:● slideshare.net/tisto
● Code:● svn.plone.org/svn/collective/examples/example.dzu
gconference/example.dzugconference
Was ist ein Test?
Test Beispiel
def is_palindrome(word):
pass
def test_palindromic_word():
assert is_palindrome("noon") == True
assert is_palindrome("foo") == False
Python Unittest Testcase
http://docs.python.org/library/unittest.html
import unittest
class TestIsPalindrome(unittest.TestCase):
def test_palindromic_word():
self.assertEquals(is_palindrome("noon"), True)
self.failIf(is_palindrome("foo"))
def test_non_string_raises_exception():
self.UnlessRaises(TypeError, is_palindrome, 2)
Warum sind Tests wichtig?
Warum Testen?
● Robusterer Code● Besseres Code Verständnis● Nachweis● Dokumentation von Software Anforderungen● „Billigeres“ Bugfixing● Refactoring
Was ist Test-Driven Development?
Test-Driven Development
Kent Beck: Test Driven Development
Arten von Tests
Funktionale Tests
● Funktionaler Ablauf● Benutzersicht● BlackBox Testing● Akzeptanztests
XP/Scrum und Funktionale Tests
● Abbildung von (Software) Anforderungen durch User Stories / Acceptance Tests
● Testbare Spezifikation● Code der die Tests besteht● => Beweist das die Software tut was sie soll
User Stories mit (Doc)Tests
As a logged-in user, I can add a new page to the website.
Beispiel Funktionaler Doctest
>>> browser.open(portal_url)
>>> browser.getLink(id='example-dzugconference-presenter').click()
>>> browser.getControl(name='form.widgets.title').value = "Presenter 1"
>>> browser.getControl(name='form.buttons.save').click()
Plone SVN: plone.app.discussion/.../presenter.txt
zope.testbrowser
● browser.open('http://nohost/plone/')● browser.getLink(link_text).click()● browser.url● browser.reload()● browser.getControl(input_name).value =
‘Whatever’● browser.getControl(name='form.buttons.save').
click()
http://pypi.python.org/pypi/zope.testbrowser
zope.testbrowser debugging
open('/tmp/testbrowser.html','w').write(browser.contents)
Interlude (Interaktives Debugging)
import interlude
suite = DocFileSuite(...,
globs={'interact': interlude.interact},
...)
>>> interact( locals() )
http://pypi.python.org/pypi/interlude/
Testing Pyramide
Integrationstests
● Testet die Zusammenarbeit voneinander abhängiger Komponenten
Beispiel Integrationstest
class IPresenter(form.Schema):
title = schema.TextLine(
title=_(u"Title")
)
...
Was wollen wir testen?
● Schema● Typ Registrierung (FTI)● Factory● Hinzufügen des Inhaltstyps● View
Integrationstests: setUp
def setUp(self):
self.portal = self.layer['portal']
setRoles(self.portal, TEST_USER_NAME, ['Manager'])
self.portal.invokeFactory('Folder', 'test-folder')
setRoles(self.portal, TEST_USER_NAME, ['Member'])
self.folder = self.portal['test-folder']
Integrationstest: Schema
def test_schema(self):
fti = queryUtility(IDexterityFTI,
name='example.dzugconference.presenter')
schema = fti.lookupSchema()
self.assertEquals(IPresenter, schema)
Integrationstest: FTI
def test_fti(self):
fti = queryUtility(IDexterityFTI,
name='example.dzugconference.presenter')
self.assertNotEquals(None, fti)
Integrationstest: Factory
def test_factory(self):
fti = queryUtility(IDexterityFTI,
name='example.dzugconference.presenter')
factory = fti.factory
new_object = createObject(factory)
self.failUnless(
IPresenter.providedBy(new_object))
Integrationstest: Hinzufügen
def test_adding(self):
self.folder.invokeFactory(
'example.dzugconference.presenter',
'presenter1')
p1 = self.folder['presenter1']
self.failUnless(IPresenter.providedBy(p1))
Integrationstest: View
def test_view(self):
self.folder.invokeFactory(
'example.dzugconference.presenter',
'presenter1')
p1 = self.folder['presenter1']
view = p1.restrictedTraverse('@@view')
self.failUnless(view)
Integrationstest: Debugging
def test_todo(self):
import pdb; pdb.set_trace()
import ipdb; ipdb.set_trace()
Testing Pyramide
Unit Tests in Plone
● Test einer isolierten Komponente● Wie?● => Mock Objects
Mock und Fake Objekte
● Externe Datenbanken● Web Services● Plone Komponenten● Netzwerkverbindungen● Benötigen externe Komponenten
http://plone.org/products/dexterity/documentation/manual/developer-manual/testing/mock-testing
http://pypi.python.org/pypi/plone.mocktestcase
Setup Tests
● Professional Plone Development● Layer / collective.testcaselayer● plone.testing / plone.app.testing
Test Setup (PPD)
TestPortlet(CinemaContentTestCase):
def afterSetUp(self):
…
...
PPD: Optilux Code Examples
Test Setup (Layer)
class CommentTest(PloneTestCase):
layer = DiscussionLayer
def afterSetUp(self):
…
...
http://svn.plone.org/svn/plone/plone.app.discussion/trunk
Test Setup (plone.testing)
class ExampleDzugConference(PloneSandboxLayer):
defaultBases = (PLONE_FIXTURE,)
def setUpPloneSite(self, portal):
…
...
Collective SVN: examples/examples.dzugconference
Testrunner
parts += test
[test]
recipe = zc.recipe.testrunner
eggs = ${instance:eggs}
defaults = ['--auto-color', '--auto-progress']
Test Geschwindigkeit
Integrations-Tests vs. Funktionale Tests
● plone.app.discussion● Test Setup (Plone Site): 9,5 Sekunden● 116 Integrationstests: 15 Sekunden (+ 6,5)● 1 Funktionaler Test: 21.5 Sekunden (+ 12,0)
Testing Pyramide
Test Abdeckung
buildout.cfg
[coverage-test]
recipe = zc.recipe.testrunner
eggs = ${test:eggs}
defaults = ['--coverage', '../../coverage', '-v', '--auto-progress']
[coverage-report]
recipe = zc.recipe.egg
eggs = z3c.coverage
arguments = ('coverage', 'report')
Hudson Continuous Integration
http://hudson.zmag.de/hudson
Weitere Tests
● Python● Pylint
● Templates● ZPTLint
● Javascript● JSLint● qUnit
svn.plone.org/svn/plone/plone.app.discussion/trunk/test-plone-4.0.x.cfg
Weiterführendes Material
● Kent Beck: Test Driven Development● Python Testing: Beginner's Guide● Tarek Ziadé: Expert Python Programming● Testing in Plone:
http://plone.org/documentation/kb/testing● Dexterity Manual - Testing:
http://plone.org/products/dexterity/documentation/manual/developer-manual/testing
Fragen?