Test setup with layers and z3c.testsetup

Tags: thehealthagency, grok, python

Update: Do read this entry, but I’ve written an update.

At The Health Agency we’re currently switching to z3c.testsetup for our test setup needs. The nice thing is that you only need one test .py file that is found by your testrunner (I suggest tests/test_setup.py) with the following content:

import z3c.testsetup
test_suite = z3c.testsetup.register_all_tests('your.package.name')

No more manual building of test suites with DocTestCases and so. Just add :doctest: to every README or .txt or docstring-in-your.py like this:

.. :doctest:

    >>> print 4
    4

For testing our libraries, simple python-level doctests are often enough. From time to time you need to call grokcore.component.grok('my.package') but that’s about it. The base package for our grok sites needs a more elaborate setup. And preferably a setup that is reusable in the grok sites that use it.

Solution for more elaborate test setups: test layers. Zope.testing provides a layer mechanism. It took me some time to understand their use, but I think I’ve got it:

  • You can tie a test to a layer. All tests that “are on the same layer” are run in one group.

  • The layer can do setup before and after the layer’s tests are run. This means a layer is useful for setting up your zodb; an test sql database; some zcml setup.

  • The layer can also do extra layer-specific setup/teardown for every individual test inside it. A layer that provides a zodb can thus arrange for a transaction.abort() to be done after every single test: test isolation.

After an all-important from zope.app.testing.functional import FunctionalTestSetup, what my layer does is:

  • In the setUp(), instantiate FunctionalTestSetup with our own ftesting.zcml.

  • In the setUp(), grok a fixtures.py with a test site object, some test user folders, test adapters and so. This makes those grokked components available to every test on this layer.

  • testSetUp() is the setup method that is called for every individual test. It calls FunctionalTestSetup’s setup that gives us a fresh clean ZODB. And it sets up a test grok site (of our own design) in the ZODB’s root.

  • On both levels there’s the corresponding tear down method.

Using layers is easy with z3c.testsetup: add a :layer: my.package.layer.

One z3c.testsetup gotcha: it internally creates testcases, so it is hard to pass in globals to the testcase (unless you do it in one go for all the tests in the entire package, which wasn’t appropriate). The solution: call a setup method by adding :setup: my.package.globsetup to your doctest. globsetup(test) can append to the test.globs dictionary. I found it a bit dirty at first, but after using it for a while it smells OK. You set the globs explicitly in those doctests where you need it. In every doctest, it is clear which extra setup methods are called and thus it is easy to look up that method to see which globals are available.

An alternative to this setup call is of course to do an from my.package.globs import * or something like that. Which seems a bit cleaner. Time for some experimentation. Oh, if someone knows how to set the globs from within a layer: please mail me.

Comment by Jasper: grok < 1.0a4 contain z3c.testsetup 0.2.1, so make sure you pin it to a more recent release like 0.4.

 
vanrees.org logo

Reinout van Rees

My name is Reinout van Rees and I program in Python, I live in the Netherlands, I cycle recumbent bikes and I have a model railway.

Weblog feeds

Most of my website content is in my weblog. You can keep up to date by subscribing to the automatic feeds (for instance with Google reader):