Intro to Testing in Zope, Plone


Published on

Testing intro, unittest, doctest, zope.testing, Zope Test Cases, Plone Test Cases

Published in: Technology
  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Intro to Testing in Zope, Plone

  1. 1. Intro to Testing in Zope, Plone Andriy Mylenkyy © Quintagroup, 2008
  2. 2. What we'll talk about <ul><li>Testing intro </li></ul><ul><li>unittest </li></ul><ul><li>doctest </li></ul><ul><li>zope.testing </li></ul><ul><li>Zope Test Cases </li></ul><ul><li>Plone Test Cases </li></ul>
  3. 3. Testing intro <ul><li>Problem </li></ul><ul><li>Types of test </li></ul><ul><ul><li>Unit tests </li></ul></ul><ul><ul><li>Integration tests </li></ul></ul><ul><ul><li>Functional tests </li></ul></ul><ul><ul><li>System tests </li></ul></ul><ul><li>Testing and documentation </li></ul>
  4. 4. unittest <ul><li>Author – Steve Purcell </li></ul><ul><li>Actually is PyUnit, which is part of the Python 2.1 standard library </li></ul><ul><li>based on Erich Gamma's JUnit and Kent Beck's Smalltalk testing framework. </li></ul>
  5. 5. TestSuite TestCase TC TC TC TestSuite(TS)
  6. 6. unittest: TestCase <ul><li>testMethodName </li></ul><ul><li>result </li></ul><ul><li>fail*/assert* </li></ul><ul><li>(failUnless|assert)Raises </li></ul><ul><li>run/debug </li></ul><ul><li>__call__ </li></ul>run(result) result.startTest() result.stopTest() testMethod() setUp() tearDown() debug
  7. 7. unittest: module members <ul><li>TestSuite </li></ul><ul><ul><li>_test (addTest, addTests) </li></ul></ul><ul><ul><li>run/debug (__call__) </li></ul></ul><ul><li>TestLoader </li></ul><ul><ul><li>define test method prefix [' test '] </li></ul></ul><ul><ul><li>sort test method function [ cmp ] </li></ul></ul><ul><ul><li>Methods : </li></ul></ul><ul><ul><ul><li>loadTestsFrom* [TestCase, Module, Name, Names] </li></ul></ul></ul><ul><ul><ul><li>getTestCaseNames -> use test method prefix for find test methods -> used for build TestCase instances </li></ul></ul></ul>
  8. 8. unittest: module members (cont.) <ul><li>TextTestRunner </li></ul><ul><li>TestProgram </li></ul><ul><li>FunctionTestCase </li></ul><ul><li>TestResult </li></ul><ul><li>Help functions : </li></ul><ul><ul><li>makeSuite , </li></ul></ul><ul><ul><li>findTestCase , </li></ul></ul><ul><ul><li>getTestCaseNames </li></ul></ul>
  9. 9. unittest: TestSuite / TestCase import unittest as ut class WidgetTC(ut.TestCase): def setUp(self): ... def tearDown(self): ... def testDefaultSize(self): assertEqual(...) def testResize(self): self.failIf(...) def otherTest(self): self.assertRaises(...) if __name__ == '__main__': # 1 # ut.main() #1b# ut.main(defaultTest='WidgetTC') #2,3# suite = ut.TestSuite() # 2 # suite.addTest(ut.makeSuite(WidgetTC)) # 3 # suite.addTest('WidgetTC('otherTest')) #2,3# runner = ut.TextTestRunner() #2,3# TestSuite _tests WidgetTC(&quot;testDefaultSize&quot;) WidgetTC(&quot;testResize&quot;) unittest.makeSuite(WidgetTC)
  10. 10. unittest: command line ---------------------------------------------- import unittest class WidgetTC(unittest.TestCase): ... if __name__ == '__main__': unittest.main() ----------------------------------------------- $ python .. OK ------------------------------------------------- $ python WidgetTC.testDefaultSize WidgetTC.testResize .. OK ------------------------------------------------- $ python -v testDefaultSize (__main__.WidgetTC) ... ok testResize (__main__.WidgetTC) ... ok ... OK
  11. 11. doctest <ul><li>Authors: Jim Fulton, Edward Loper </li></ul><ul><li>part of the Python standard library since version 2.1. </li></ul>
  12. 12. import types, mymodule, doctest def hasInt(*args): &quot;&quot;&quot; Test is integer value present in args. >>> hasInt(&quot;the string&quot;) False >>> hasInt(&quot;the string&quot;, 1) True &quot;&quot;&quot; args = list(args) while args: if type(args.pop()) == types.IntType: return True return False __test__ = {'My module test' : mymodule, 'My string test' : “””>>>1==1 True“””} if __name__ == '__main__': doctest.testmod() doctest: bases <ul><li>Python shell with explanations </li></ul><ul><li>Docstrings </li></ul><ul><li>Separate file </li></ul><ul><li>__test__ in module </li></ul>
  13. 13. doctest: testmod/testfile <ul><li>testmod () </li></ul><ul><ul><ul><li>m= None </li></ul></ul></ul><ul><ul><ul><li>globs= None </li></ul></ul></ul><ul><ul><ul><li>optionflags= 0 </li></ul></ul></ul><ul><ul><ul><li>verbose=None, report=True, raise_on_error=False </li></ul></ul></ul><ul><li>testfile (filename) </li></ul><ul><ul><ul><li>module_relative=True, package=None </li></ul></ul></ul><ul><ul><ul><li>parser=DocTestParser() </li></ul></ul></ul><ul><ul><ul><li>same to testmod </li></ul></ul></ul><ul><li>run_docstring_examples (f, globs) </li></ul><ul><ul><ul><li>compileflags=None </li></ul></ul></ul>
  14. 14. doctest: Option flags <ul><li>COMPARISON_FLAGS </li></ul><ul><li>DONT_ACCEPT_TRUE_FOR_1, DONT_ACCEPT_BLANKLINE, NORMALIZE_WHITESPACE, ELLIPSIS, IGNORE_EXCEPTION_DETAIL </li></ul><ul><li>REPORTING_FLAGS </li></ul><ul><li>REPORT_ONLY_FIRST_FAILURE, REPORT_*DIFF </li></ul><ul><li>`want` strings markers </li></ul><ul><li>BLANKLINE_MARKER='<BLANKLINE>', ELLIPSIS_MARKER = '...' </li></ul>Test ELLIPSIS option >>> range(100) #doctest:+ELLIPSIS [0, ..., 99] >>> range(100) [0, ..., 99] import doctest “”” >>> range(100) [0, ..., 99] “”” doctest.testmod( optionflags=doctest.ELLIPSIS)
  15. 15. doctest: Unittest Support <ul><li>DocTestSuite </li></ul><ul><ul><li>Common options: module, globs, extraglobs, test_finder </li></ul></ul><ul><ul><li>**options: setUp, tearDown, globs, optionflags </li></ul></ul><ul><li>DocFileTest(path) </li></ul><ul><ul><li>module_relative=True, package=None </li></ul></ul><ul><li>set_unittest_reportflags. Only reporting </li></ul>“”” >>> range(100) [0, ..., 99] “”” import doctest, unittest if __name__ == “__main__”: suite = unittest.TestSuite() testrunner = unittest.TextTestRunner() #
  16. 16. doctest: Internal structure object DocTes t Example Example Example result DocTestFinder DocTestRunner
  17. 17. zope.testing <ul><li>testrunner for run automated tests </li></ul><ul><li>primary feature - it finds tests in directory trees </li></ul><ul><li>highly customizable </li></ul><ul><li>Test filtering </li></ul><ul><li>Organization of tests into levels and layers </li></ul><ul><li>Reporting </li></ul><ul><li>Analysis of test execution </li></ul>
  18. 18. zope.testing: Lyaers <ul><li>Minimize the number of invocations </li></ul><ul><li>TestCase/TestSuite associate with layer </li></ul><ul><li>Classes with setUp and tearDown class methods </li></ul><ul><li>Nested Layers </li></ul><ul><li>testSetUp/testTearDown </li></ul><ul><li>-l --layer=<layer name> </li></ul><ul><li>“Unit” layer (-u) / not unit (-f) </li></ul>setUp tearDown setUp tearDown TestCase TestCase TestCase
  19. 19. zope.testing: Lyaers (cont) class BaseLayer : @classmethod def setUp (cls): “ Layer setup ” @classmethod def tearDown (cls): “ Layer teardown ” @classmethod def testSetUp (cls): “ Before each test setup ” @classmethod def testTearDown (cls): “ After each test teardown ” class MyTestCase(TestCase): layer = NestedLayer def setUp(self): pass def tearDown(self): pass class NestedLayer( BaseLayer) : @classmethod def setUp (cls): “ Layer setup ” @classmethod def tearDown (cls): “ Layer teardown ” --path', directory_with_tests --layer 112 --layer Unit'.split
  20. 20. zope.testing: Levels <ul><li>Additional filter – alow run tests for specific level </li></ul><ul><li>TestCase/TestInteger attribute 'level' integer >0 </li></ul><ul><li>Test runner can be configured to only run tests at a specific level or below by default. </li></ul><ul><li>Additional filter – alow run tests for specific level </li></ul><ul><li>TestCase/TestInteger attribute 'level' integer >0 </li></ul><ul><li>Test runner can be configured to only run tests at a specific level or below by default. </li></ul><ul><li>If no level -> assumed level 1 </li></ul><ul><li>by default, only tests at level 1 </li></ul><ul><li>--at-level (-a) / --all </li></ul>TestCase TestCase TestCase TestCase TestCase TestCase Level 1 Level 2 TestCase TestCase TestCase Level - 3
  21. 21. zope.testing: Test selection <ul><li>Search for </li></ul><ul><ul><li>Package (-s, --package, --dir) </li></ul></ul><ul><ul><li>Module (-m, --module) </li></ul></ul><ul><ul><li>Test within the module (-t, --test ) </li></ul></ul><ul><li>layer (-l, --level) & level (-a, --at-level / --all ) </li></ul><ul><li>--tests-pattern (--tests-pattern ^tests$ ) </li></ul><ul><li>--test-suite ( ' test_suite ' by default) </li></ul><ul><li>Module & Test can be re.expr, positional args </li></ul>
  22. 22. zope.testing: options (cont) <ul><li>Where search for tests </li></ul><ul><ul><li>--path </li></ul></ul><ul><ul><li>--test-path / --package-path --> not add to sys.path </li></ul></ul><ul><li>--progress (-p). -v (with test descriptions) </li></ul><ul><li>--coverage=<coverage_dir> - where put coverage reports </li></ul><ul><li>--profile </li></ul><ul><li>--repeat </li></ul><ul><li>--verbose (-v) / --quiet (-q) </li></ul>
  23. 23. zope.testing: script <ul><li>Wrapper around zope.testing.testrunner </li></ul><ul><li>Extends testrunner with options: </li></ul><ul><ul><li>--config-file </li></ul></ul><ul><ul><li>--nowarnings </li></ul></ul><ul><li>Prepare default options list for call with testrunner: </li></ul><ul><ul><li>--path = INSTANCE_HOME/python/lib </li></ul></ul><ul><ul><li>--package-path products Products </li></ul></ul><ul><li>call </li></ul>
  24. 24. zope.testing: examples <ul><li>$ZH/bin/ --config-file=$IH/etc/zope.conf </li></ul><ul><ul><li>--path=~/Plone/lib/python </li></ul></ul><ul><ul><li>--package-path ~/Plone/Products Products </li></ul></ul><ul><ul><li>-s Products.CMFQuickInstallerTool </li></ul></ul><ul><li>$INSTANCE_HOME/bin/zopectl test </li></ul><ul><li>-s Products.CMFQuickInstallerTool </li></ul>
  25. 25. ZopeTestCase(ZTC) <ul><li>Author : Stefan H. Holek </li></ul><ul><li>ZTC Goals </li></ul><ul><ul><li>Simplify distribute tests with Zope products. </li></ul></ul><ul><ul><li>Be as close to “real” Zope as possible. </li></ul></ul><ul><ul><li>Be as simple to use as unittest. </li></ul></ul><ul><li>Decisions </li></ul><ul><ul><li>Base ZTC on Testing and improve on it </li></ul></ul><ul><ul><li>Provide a “batteries included“ Zope test sandbox </li></ul></ul><ul><ul><li>Support for testing Zope security. </li></ul></ul>
  26. 26. ZTC: vs <ul><li> now depricated </li></ul><ul><ul><li>bin/ –config-file=.../zope.conf ... </li></ul></ul><ul><li>BUT: </li></ul><ul><ul><li>Supports in the tests directory </li></ul></ul><ul><ul><li>Allows to connect to a running ZEO server </li></ul></ul>import os from ZODB.DemoStorage import DemoStorage from ZODB.FileStorage import FileStorage db = os.path.join('..', '..', '..', 'var', 'Data.fs') db = os.path.abspath(db) Storage = DemoStorage(base=FileStorage(db, read_only=1))
  27. 27. ZTC: <ul><li>ZopeTestCase class </li></ul><ul><li>ZopeTestCase sets up a default fixture </li></ul><ul><li>Handles opening and closing of ZODB con. </li></ul><ul><li>Handles transactions </li></ul><ul><li>Provides a REQUEST object </li></ul><ul><li>API to modify the test user’s credentials </li></ul>
  28. 28. ZTC: Entrypoints setUp(self) try: self. beforeSetUp() = self._app() self._setup() self. afterSetUp() except: self._clear() raise tearDown(self) try: self. beforeTearDown() self._clear(1) except: self._clear() raise Before ALL ZTC Folder, UserFolder, User, login After Fixture created ConnectionRegistry Before delete folder from After folder delete Before close connection to ZODB beforeClose() afterClear() After ALL Zope.APP Opens a ZODB connection Request.__of__(app)
  29. 29. ZTC: Class diagram
  30. 30. ZTC: Writing Tests <ul><li>Create a test case class derived from ZopeTestCase.ZopeTestCase </li></ul><ul><li>Override afterSetUp() / tearDown() to add the objects you want to test, clearing data </li></ul><ul><li>Write one or more tests exercising these objects </li></ul><ul><li>Define test_suite object, which return unittest.TestSuite object </li></ul>
  31. 31. ZTC: Example <ul><li>import unittest </li></ul><ul><li>from Testing import ZopeTestCase </li></ul><ul><li>ZopeTestCase.installProduct(’SomeProduct’) </li></ul><ul><li>class MyTest(ZopeTestCase.ZopeTestCase): </li></ul><ul><li>def afterSetUp(self): </li></ul><ul><li>self.folder.addDTMLMethod(’foo’) </li></ul><ul><li>def testFoo(self): </li></ul><ul><li>assert hasattr(self.folder, ’foo’) </li></ul><ul><li>def test_suite(): </li></ul><ul><li>return unittest.TestSuite( </li></ul><ul><li>[unittest.makeSuite(MyTest),] ) </li></ul>
  32. 32. PortalTestCase <ul><li>Almost ZopeTestCase </li></ul><ul><li>portal object (self.portal) </li></ul><ul><li>user folder (UF) inside the portal </li></ul><ul><li>portal object must create in Subclasses </li></ul><ul><li>getPortal() returns a usable portal object </li></ul><ul><li>Defaults: </li></ul><ul><ul><li>user with role 'Member' inside the UF </li></ul></ul><ul><ul><li>default user's memberarea (self.folder) </li></ul></ul><ul><ul><li>default user is logged in </li></ul></ul>
  33. 33. PortalTestCase (cont) <ul><li>portal = 'portal' </li></ul>setUp(self) try: self. beforeSetUp() = self._app() self.portal = self._portal() self._setup() self. afterSetUp() except: self._clear() raise _portal(self) return self.getPortal() getPortal(self) return getattr(, poratl)
  34. 34. PloneTestCase(PTC) <ul><li>PloneTestCase class </li></ul><ul><li>Usefull methods: </li></ul><ul><ul><li>hasProduct. installProduct </li></ul></ul><ul><ul><li>hasPackage, installPackage </li></ul></ul><ul><ul><li>setupPloneSite(products, extension_profiles) </li></ul></ul><ul><li>Constants: </li></ul><ul><ul><li>portal_*, default_* </li></ul></ul><ul><ul><li>PLONE2. x , PLONE3. x , PLONE4. x </li></ul></ul>
  35. 35. PTC: PloneTestCase (cont) <ul><li>Plone installs in module </li></ul><ul><li>ZCMLLayer, PloneSiteLayer(ZCML) layers </li></ul><ul><ul><li>onsetup, onteardown functions. </li></ul></ul><ul><li>Extend PortalTestCase security with methods: </li></ul><ul><ul><li>setGroups, loginAsPortalOwner </li></ul></ul>
  36. 36. PTC: PloneTestCase example import os, sys if __name__ == '__main__': execfile(os.path.join(sys.path[0], '')) from Products.PloneTestCase import PloneTestCase PloneTestCase.setupPloneSite() class TestDocument(PloneTestCase.PloneTestCase): def afterSetUp(self): ... def testAddDocument(self): self.failUnless(...) def test_suite(): ... return suite if __name__ == '__main__': framework()
  37. 37. Resources <ul><li>Sources ;) </li></ul><ul><li> </li></ul><ul><li> </li></ul><ul><li> </li></ul><ul><li> </li></ul><ul><li> </li></ul><ul><li> </li></ul>