Working with paster-generated test suites
Products created with paster already come with four different types of test suites ready to be used:
- Unit test in doctest syntax
- Unit test inside the method's docstring
- Integration test in doctest syntax
- Functional test in doctest syntax
The automatically generated tests
module provides the necessary code to run the test cases.
Getting ready
We are going to use the same egg-structured product we created with paster
in the Installation of the product recipe. If you didn't read that one, you can use the example code available for download on the Packt Publishing website.
How to do it...
Open the tests
module in Products.poxContentTypes/Products/poxContentTypes
to see four commented test suites:
... def test_suite(): return unittest.TestSuite([ # Unit tests #doctestunit.DocFileSuite( # 'README.txt', package='Products.poxContentTypes', # setUp=testing.setUp, tearDown=testing.tearDown), #doctestunit.DocTestSuite( # module='Products.poxContentTypes.mymodule', # setUp=testing.setUp, tearDown=testing.tearDown), # Integration tests that use PloneTestCase #ztc.ZopeDocFileSuite( # 'README.txt', package='Products.poxContentTypes', # test_class=TestCase), #ztc.FunctionalDocFileSuite( # 'browser.txt', package='Products.poxContentTypes', # test_class=TestCase), ]) ...
How it works...
By uncommenting the testing code above and writing some test code, we will learn each of the above testing variations.
The first testing technique available in test_suite
method is a unit test that should be defined in a README.txt
file inside the package we are developing.
Uncomment the lines:
doctestunit.DocFileSuite( 'README.txt', package='Products.poxContentTypes', setUp=testing.setUp, tearDown=testing.tearDown),
Create a README.txt
file inside the package. If you are using the code from the previous chapter, the file should be created in ./src/Products.poxContentTypes/Products/poxContentTypes/
in your buildout directory.
Then add the following lines:
>>> from Products.poxContentTypes import config
>>> from Products.poxContentTypes.content.XNewsItem import XNewsItem
>>> xni=XNewsItem('dummy')
>>> xni
<XNewsItem at dummy>
>>> xni.countryVocabulary()
(('AQ', 'Antartiqa'), ('AR', 'Argentina'), ('BS', 'Bahamas'), ('BR', 'Brazil'), ('CM', 'Cameroon'), ('CL', 'Chile'))
After the initial imports, we create an instance of our XNewsItem
class. Bear in mind that this is not the way objects are created in a Plone site. But because we don't want to test our class interacting in a Plone site but the countryVocabulary
method (this is a unit test, not an integration one), we just need a proper class' instance. On creation, we add the dummy
argument because all Archetypes objects need an id
as an initialization parameter.
After that, we just call the method in question to check if its returned values are the expected ones.
Now we have to run all the tests (just one for the time being) in the package.
Inside your instance folder run this command:
./bin/instance test -s Products.poxContentTypes
We use the -s
option to specify the package we want to test. By default Zope seeks a tests
module or package inside the packages to be tested.
When done, we'll get an output similar to the following screenshot:
We could have written this unit test even if we hadn't known anything about Plone but plain Python: we have instantiated the class and executed a method.
The second testing technique available in test_suite
method is, again, a unit test. But we won't create any external file this time; tests will be written in docstrings.
Uncomment the following code and replace mymodule
with the module we are testing:
doctestunit.DocTestSuite(
module='Products.poxContentTypes.content.XNewsItem',
setUp=testing.setUp, tearDown=testing.tearDown),
Now modify the countryVocabulary
method's docstring by adding the same test as in the previous section. The file to be modified is XNewsItem.py
in src/Products.poxContentTypes/Products/poxContentTypes/content
.
security.declarePublic('countryVocabulary') def countryVocabulary(self): """ Returns a list of country codes and their names for the"country" field >>> from Products.poxContentTypes import config >>> from Products.poxContentTypes.content.XNewsItem importXNewsItem >>> xni=XNewsItem('dummy') >>> xni <XNewsItem at dummy> >>> xni.countryVocabulary() (('AQ', 'Antartiqa'), ('AR', 'Argentina'), ('BS', 'Bahamas'),('BR', 'Brazil'), ('CM', 'Cameroon'), ('CL', 'Chile')) """ return ( ('AQ', 'Antartiqa'), ('AR', 'Argentina'), ('BS', 'Bahamas'), ('BR', 'Brazil'), ('CM', 'Cameroon'), ('CL', 'Chile') )
As you can see, comments can be inserted together with testing code. This is the way doctests meet the "doc" part of their name.
Now, again test the package by running:
./bin/instance test -s Products.poxContentTypes
You should get an output as shown in the following screenshot:
The third testing technique available in test_suite
method is an integration test. This means that we are going to test our class as a component of a bigger environment: a Plone site.
Uncomment the next group of lines and replace README.txt
with INTEGRATION.txt
: we'll create a new file instead of overwriting our first unit test.
ztc.ZopeDocFileSuite( 'INTEGRATION.txt', package='Products.poxContentTypes', test_class=TestCase),
Integration tests based on the PloneTestCase
class set a whole Plone site on the fly. For this particular one, we'll need our product to be installed, so open the tests.py
file and change the s
etupPloneSite
call after initial imports and replace it with the following lines of code:
# ptc.setupPloneSite() ztc.installProduct('poxContentTypes') ptc.setupPloneSite(products=['poxContentTypes',])
Then create a new INTEGRATION.txt
file inside your package folder, which is ./src/Products.poxContentTypes/Products/poxContentTypes/
in your buildout folder.
>>> self.loginAsPortalOwner()
>>> portal.invokeFactory('XNewsItem', 'dummy')
'dummy'
>>> portal.dummy
<XNewsItem at /plone/dummy>
>>> portal.dummy.countryVocabulary()
(('AQ', 'Antartiqa'), ('AR', 'Argentina'), ('BS', 'Bahamas'), ('BR', 'Brazil'), ('CM', 'Cameroon'), ('CL', 'Chile'))
Spot the big difference with the previous unit tests regarding initialization of our objects. We are now using the in
vokeFactory
method in the portal
object to create an instance of our XNewsItem
class. Furthermore, we need special permissions to create content in a Plone portal
, that's why we first loginAsPortalOwner()
.
We could have added a line like:
xni=XNewsItem(‘dummy’)
This would use xni
as in the previous examples. However, we wanted to emphasize the integration nature of this test by using the portal
object repeatedly.
Note
If you have any iPython-related egg (ipython
, ipdb
, or iw.debug
) installed in your instance, you would probably want to uninstall them by commenting their lines in the buildout.cfg
file. iPython changes the way of printing into the standard output, so the expected output won't match the actual one.
Test the package again by running:
./bin/instance test -s Products.poxContentTypes
You should get an output like the following (we have removed several Zope warnings during instance start up):
Execution time now is much longer than in previous test runs. This is due to the recently added integration test because a new full Plone site is being automatically created.
We'll cover how to create functional tests later on in this chapter. Yet, we can also run them with the paster-created test suite. Uncomment the following lines if you want to run a functional test defined in the browser.txt
file:
ztc.FunctionalDocFileSuite( 'browser.txt', package='Products.poxContentTypes', test_class=TestCase),
See also
- Installation of the product