Skip to content. | Skip to navigation

Sections
Personal tools
What is this?
Hi, my name is Tom Lazar and I'm a Plone and Zope developer based in Berlin, Germany and this is my personal and professional (no big difference, really...) website.
 

Integrating pure Python tests with PloneTestCase

Filed Under:

Running tests takes long enough as it is...

Often, when writing a Plone Product I come across small methods that I need to implement, that actually have no dependency upon Plone itself but are too trivial and/or specific as to warrant putting them into a separate package. If that's the case for you, too, you will undoubtedly start to get impatient when testing these methods, because with each friggin' testrun an entire zope instance is created and torn down again. This I have become used to and accept it as part of life as Zope developer, if the code I'm testing actually needs that setup. But it just kept bugging me that I need to wait so long for tests that actually execute in under one second. So I've poked around today and came up with the following scenario:

  1. put your methods into a separate module that has no import dependencies from zope
  2. add a test runner to that module to execute its tests without starting up zope
  3. create a test suite for your product that will pick up the tests of your module

1. standalone module

For example, I've created a (crude) helper for batching some items in a file named batching.py on the root level of my Product (along with a little doctest testing some arbitrary edge cases):
def batch(items=[], batch=0, batchsize=5):
"""returns a batch from the given items

>>> items = range(12)
>>> batch(items, 3, 5)
Traceback (most recent call last):
...
IndexError: No batch number 3

>>> batch(items, 0, 5)
[0, 1, 2, 3, 4]

>>> batch(items, 1, 5)
[5, 6, 7, 8, 9]

>>> batch(items, 2, 5)
[10, 11]

>>> batch(items, 2, 4)
[8, 9, 10, 11]
"""
if batch >= batchnum(len(items), batchsize):
raise IndexError, \
"No batch number %d" % batch
else:
return items[batch * batchsize:(batch+1) * batchsize]
I can use this method in my product with the following import statement:
from Products.PRODUCTNAME.batching import batch

2. add a test runner

Here you just need to follow the straightforward example given in the doctest documentation and add the following snippet to the bottom of batching.py:
if __name__ == "__main__":
import doctest
doctest.testmod()
Now you can simply execute your module and it will run its tests at blazing speed - buckle up, Dorothy... This definitely makes for much nicer test driven development!

3. Zope integration

Now we just need to make sure, that the tests will also be executed, when we run our Product's entire suite. This is done by writing a light-weight  Testcase based on Zope's DocTestSuite that 'picks' up the standalone tests. I've named mine 'testUtilities.py' and stuck it right into my Product's 'test' folder:
import unittest
from unittest import TestSuite
from zope.testing.doctestunit import DocTestSuite

def test_suite():
return TestSuite((
DocTestSuite('Products.PRODUCTNAME.batching'),
))

if __name__ == '__main__':
unittest.main(defaultTest="test_suite")
Edit: removed some superflous code in the examples.