From ec75d934d95361222bd79a334ff24f1e347a73df Mon Sep 17 00:00:00 2001 From: stevepiercy Date: Wed, 4 Dec 2024 06:32:57 -0800 Subject: [PATCH] [fc] Repository: plone.app.theming Branch: refs/heads/master Date: 2024-12-03T19:42:58-08:00 Author: Steve Piercy (stevepiercy) Commit: https://github.com/plone/plone.app.theming/commit/1933dd8b1bb162ac71500cc0fa6d773572fad93d Update link and label to Theming of Classic UI documentation See https://github.com/plone/Products.CMFPlone/pull/4054 Files changed: M src/plone/app/theming/browser/controlpanel.pt Repository: plone.app.theming Branch: refs/heads/master Date: 2024-12-03T20:09:51-08:00 Author: Steve Piercy (stevepiercy) Commit: https://github.com/plone/plone.app.theming/commit/dc15462a8c93c7c0467aee5b4767a412884a1b2e Create 248.bugfix Files changed: A news/248.bugfix Repository: plone.app.theming Branch: refs/heads/master Date: 2024-12-04T06:32:57-08:00 Author: Steve Piercy (stevepiercy) Commit: https://github.com/plone/plone.app.theming/commit/907d61d66eab7c649ed0b2f5c02e3dd3e57e319d Merge pull request #248 from plone/docs-classic-ui-theming Update link and label to Theming of Classic UI documentation Files changed: A news/248.bugfix M src/plone/app/theming/browser/controlpanel.pt --- last_commit.txt | 83 ++++++++++++++----------------------------------- 1 file changed, 24 insertions(+), 59 deletions(-) diff --git a/last_commit.txt b/last_commit.txt index 6c522f22c5..df8f4e3dbb 100644 --- a/last_commit.txt +++ b/last_commit.txt @@ -1,85 +1,50 @@ -Repository: Products.PortalTransforms +Repository: plone.app.theming Branch: refs/heads/master -Date: 2024-11-28T11:26:14-03:00 -Author: Peter Mathis (petschki) -Commit: https://github.com/plone/Products.PortalTransforms/commit/9125a301b3af48c293858be89a1c30817da583d8 +Date: 2024-12-03T19:42:58-08:00 +Author: Steve Piercy (stevepiercy) +Commit: https://github.com/plone/plone.app.theming/commit/1933dd8b1bb162ac71500cc0fa6d773572fad93d -Fix removed `unittest.makeSuite` in python 3.13 +Update link and label to Theming of Classic UI documentation -Files changed: -M Products/PortalTransforms/tests/output/demo1.html -M Products/PortalTransforms/tests/output/demo1.html.nofilename -M Products/PortalTransforms/tests/test_intelligenttext.py -M Products/PortalTransforms/tests/test_transforms.py -M docs/dev_manual.rst - -b'diff --git a/Products/PortalTransforms/tests/output/demo1.html b/Products/PortalTransforms/tests/output/demo1.html\nindex 2f60f41..5457311 100644\n--- a/Products/PortalTransforms/tests/output/demo1.html\n+++ b/Products/PortalTransforms/tests/output/demo1.html\n@@ -1 +1 @@\n-Chapter 44
Writing Basic Unit Tests
Di\xef\xac\x83culty
Newcomer
Skills
\xe2\x80\xa2 All you need to know is some Python.
Problem/Task
As you know by now, Zope 3 gains its incredible stability from testing any code in great detail. The
currently most common method is to write unit tests. This chapter introduces unit tests \xe2\x80\x93 which
are Zope 3 independent \xe2\x80\x93 and introduces some of the subtleties.
Solution
44.1
Implementing the Sample Class
Before we can write tests, we have to write some code that we can test. Here, we will implement
a simple class called Sample with a public attribute title and description that is accessed
via getDescription() and mutated using setDescription(). Further, the description must be
either a regular or unicode string.
Since this code will not depend on Zope, open a \xef\xac\x81le named test sample.py anywhere and add
the following class:
1 Sample(object):
2
"""A trivial Sample object."""
3
4
title = None
5
6
def __init__(self):
7
"""Initialize object."""
8
self._description = \xe2\x80\x99\xe2\x80\x99
9
1

2
CHAPTER 44. WRITING BASIC UNIT TESTS
10
def setDescription(self, value):
11
"""Change the value of the description."""
12
assert isinstance(value, (str, unicode))
13
self._description = value
14
15
def getDescription(self):
16
"""Change the value of the description."""
17
return self._description
Line 4: The title is just publicly declared and a value of None is given. Therefore this is just
a regular attribute.
Line 8: The actual description string will be stored in description.
Line 12: Make sure that the description is only a regular or unicode string, like it was stated in
the requirements.
If you wish you can now manually test the class with the interactive Python shell. Just start
Python by entering python in your shell prompt. Note that you should be in the directory in
which test sample.py is located when starting Python (an alternative is of course to specify the
directory in your PYTHONPATH.)
1 >>> from test_sample import Sample
2 >>> sample = Sample()
3 >>> print sample.title
4 None
5 >>> sample.title = \xe2\x80\x99Title\xe2\x80\x99
6 >>> print sample.title
7 Title
8 >>> print sample.getDescription()
9
10 >>> sample.setDescription(\xe2\x80\x99Hello World\xe2\x80\x99)
11 >>> print sample.getDescription()
12 Hello World
13 >>> sample.setDescription(None)
14 Traceback (most recent call last):
15
File "<stdin>", line 1, in ?
16
File "test_sample.py", line 31, in setDescription
17
assert isinstance(value, (str, unicode))
18 AssertionError
As you can see in the last test, non-string object types are not allowed as descriptions and an
AssertionError is raised.
44.2
Writing the Unit Tests
The goal of writing the unit tests is to convert this informal, manual, and interactive testing session
into a formal test class. Python provides already a module called unittest for this purpose, which
is a port of the Java-based unit testing product, JUnit, by Kent Beck and Erich Gamma. There are
three levels to the testing framework (this list deviates a bit from the original de\xef\xac\x81nitions as found
in the Python library documentation. 1).
1 http://www.python.org/doc/current/lib/module-unittest.html

44.2. WRITING THE UNIT TESTS
3
The smallest unit is obviously the \xe2\x80\x9ctest\xe2\x80\x9d, which is a single method in a TestCase class that
tests the behavior of a small piece of code or a particular aspect of an implementation. The \xe2\x80\x9ctest
case\xe2\x80\x9d is then a collection tests that share the same setup/inputs. On top of all of this sits the \xe2\x80\x9ctest
suite\xe2\x80\x9d which is a collection of test cases and/or other test suites. Test suites combine tests that
should be executed together. With the correct setup (as shown in the example below), you can
then execute test suites. For large projects like Zope 3, it is useful to know that there is also the
concept of a test runner, which manages the test run of all or a set of tests. The runner provides
useful feedback to the application, so that various user interfaces can be developed on top of it.
But enough about the theory. In the following example, which you can simply put into the same
\xef\xac\x81le as your code above, you will see a test in common Zope 3 style.
1 import unittest
2
3 class SampleTest(unittest.TestCase):
4
"""Test the Sample class"""
5
6
def test_title(self):
7
sample = Sample()
8
self.assertEqual(sample.title, None)
9
sample.title = \xe2\x80\x99Sample Title\xe2\x80\x99
10
self.assertEqual(sample.title, \xe2\x80\x99Sample Title\xe2\x80\x99)
11
12
def test_getDescription(self):
13
sample = Sample()
14
self.assertEqual(sample.getDescription(), \xe2\x80\x99\xe2\x80\x99)
15
sample._description = "Description"
16
self.assertEqual(sample.getDescription(), \xe2\x80\x99Description\xe2\x80\x99)
17
18
def test_setDescription(self):
19
sample = Sample()
20
self.assertEqual(sample._description, \xe2\x80\x99\xe2\x80\x99)
21
sample.setDescription(\xe2\x80\x99Description\xe2\x80\x99)
22
self.assertEqual(sample._description, \xe2\x80\x99Description\xe2\x80\x99)
23
sample.setDescription(u\xe2\x80\x99Description2\xe2\x80\x99)
24
self.assertEqual(sample._description, u\xe2\x80\x99Description2\xe2\x80\x99)
25
self.assertRaises(AssertionError, sample.setDescription, None)
26
27
28 def test_suite():
29
return unittest.TestSuite((
30
unittest.makeSuite(SampleTest),
31
))
32
33 if __name__ == \xe2\x80\x99__main__\xe2\x80\x99:
34
unittest.main(defaultTest=\xe2\x80\x99test_suite\xe2\x80\x99)
Line 3\xe2\x80\x934: We usually develop test classes which must inherit from TestCase. While often not
done, it is a good idea to give the class a meaningful docstring that describes the purpose of the
tests it includes.
Line 6, 12 & 18: When a test case is run, a method called runTests() is executed. While it
is possible to override this method to run tests di\xef\xac\x80erently, the default option will look for any
method whose name starts with test and execute it as a single test. This way we can create
a \xe2\x80\x9ctest method\xe2\x80\x9d for each aspect, method, function or property of the code to be tested. This
default is very sensible and is used everywhere in Zope 3.

4
CHAPTER 44. WRITING BASIC UNIT TESTS
Note that there is no docstring for test methods. This is intentional. If a docstring is speci\xef\xac\x81ed,
it is used instead of the method name to identify the test. When specifying a docstring, we have
noticed that it is very di\xef\xac\x83cult to identify the test later; therefore the method name is a much
better choice.
Line 8, 10, 14, . . . : The TestCase class implements a handful of methods that aid you with the
testing. Here are some of the most frequently used ones. For a complete list see the standard
Python documentation referenced above.
\xe2\x80\xa2 assertEqual(first,second[,msg])
Checks whether the first and second value are equal. If the test fails, the msg or None
is returned.
\xe2\x80\xa2 assertNotEqual(first,second[,msg])
This is simply the opposite to assertEqual() by checking for non-equality.
\xe2\x80\xa2 assertRaises(exception,callable,...)
You expect the callable to raise exception when executed. After the callable you can
specify any amount of positional and keyword arguments for the callable. If you expect
a group of exceptions from the execution, you can make exception a tuple of possible
exceptions.
\xe2\x80\xa2 assert (expr[,msg])
Assert checks whether the speci\xef\xac\x81ed expression executes correctly. If not, the test fails and
msg or None is returned.
\xe2\x80\xa2 assertEqual()
This testing method is equivalent to assertEqual().
\xe2\x80\xa2 assertTrue(expr[,msg])
This method is equivalent to assert (expr[,msg]).
\xe2\x80\xa2 assertFalse()
This is the opposite to assertTrue().
\xe2\x80\xa2 fail([msg])
Fails the running test without any evaluation. This is commonly used when testing various
possible execution paths at once and you would like to signify a failure if an improper path
was taken.
Line 6\xe2\x80\x9310: This method tests the title attribute of the Sample class. The \xef\xac\x81rst test should
be of course that the attribute exists and has the expected initial value (line 8). Then the title
attribute is changed and we check whether the value was really stored. This might seem like
overkill, but later you might change the title in a way that it uses properties instead. Then it
becomes very important to check whether this test still passes.
Line 12\xe2\x80\x9316: First we simply check that getDescription() returns the correct default value.
Since we do not want to use other API calls like setDescription() we set a new value of the
description via the implementation-internal description attribute (line 15). This is okay! Unit
tests can make use of implementation-speci\xef\xac\x81c attributes and methods. Finally we just check that
the correct value is returned.

44.3. RUNNING THE TESTS
5
Line 18\xe2\x80\x9325: On line 21\xe2\x80\x9324 it is checked that both regular and unicode strings are set correctly.
In the last line of the test we make sure that no other type of objects can be set as a description
and that an error is raised.
28\xe2\x80\x9331: This method returns a test suite that includes all test cases created in this module. It is
used by the Zope 3 test runner when it picks up all available tests. You would basically add the
line unittest.makeSuite(TestCaseClass) for each additional test case.
33\xe2\x80\x9334: In order to make the test module runnable by itself, you can execute unittest.main()
when the module is run.
44.3
Running the Tests
You can run the test by simply calling pythontest sample.py from the directory you saved the
\xef\xac\x81le in. Here is the result you should see:
.
--------------------------------------------------------------------
n 3 tests in 0.001s
The three dots represent the three tests that were run. If a test had failed, it would have been
reported pointing out the failing test and providing a small traceback.
When using the default Zope 3 test runner, tests will be picked up as long as they follow some
conventions.
\xe2\x80\xa2 The tests must either be in a package or be a module called tests.
\xe2\x80\xa2 If tests is a package, then all test modules inside must also have a name starting with test,
as it is the case with our name test sample.py.
\xe2\x80\xa2 The test module must be somewhere in the Zope 3 source tree, since the test runner looks
only for \xef\xac\x81les there.
In our case, you could simply create a tests package in ZOPE3/src (do not forget the
init .
py \xef\xac\x81le). Then place the test sample.py \xef\xac\x81le into this directory.
You you can use the test runner to run only the sample tests as follows from the Zope 3 root
directory:
python test.py -vp tests.test_sample
The -v option stands for verbose mode, so that detailed information about a test failure is
provided. The -p option enables a progress bar that tells you how many tests out of all have been
completed. There are many more options that can be speci\xef\xac\x81ed. You can get a full list of them with
the option -h: pythontest.py-h.
The output of the call above is as follows:
nfiguration file found.
nning UNIT tests at level 1
nning UNIT tests from /opt/zope/Zope3
3/3 (100.0%): test_title (tests.test_sample.SampleTest)
--------------------------------------------------------------------
n 3 tests in 0.002s

6
CHAPTER 44. WRITING BASIC UNIT TESTS
nning FUNCTIONAL tests at level 1
nning FUNCTIONAL tests from /opt/zope/Zope3
--------------------------------------------------------------------
n 0 tests in 0.000s
Line 1: The test runner uses a con\xef\xac\x81guration \xef\xac\x81le for some setup. This allows developers to use
the test runner for other projects as well. This message simply tells us that the con\xef\xac\x81guration \xef\xac\x81le
was found.
Line 2\xe2\x80\x938: The unit tests are run. On line 4 you can see the progress bar.
Line 9\xe2\x80\x9315: The functional tests are run, since the default test runner runs both types of tests.
Since we do not have any functional tests in the speci\xef\xac\x81ed module, there are no tests to run. To
just run the unit tests, use option -u and -f for just running the functional tests. See \xe2\x80\x9cWriting
Functional Tests\xe2\x80\x9d for more details on functional tests.

44.3. RUNNING THE TESTS
7
Exercises
1. It is not very common to do the setup \xe2\x80\x93 in our case sample=Sample() \xe2\x80\x93 in every test
method. Instead there exists a method called setUp() and its counterpart tearDown that
are run before and after each test, respectively. Change the test code above, so that it uses
the setUp() method. In later chapters and the rest of the book we will frequently use this
method of setting up tests.
2. Currently the test setDescription() test only veri\xef\xac\x81es that None is not allowed as input
value.
(a) Improve the test, so that all other builtin types are tested as well.
(b) Also, make sure that any objects inheriting from str or unicode pass as valid values.

\n+Chapter 44
Writing Basic Unit Tests
Di\xef\xac\x83culty
Newcomer
Skills
\xe2\x80\xa2 All you need to know is some Python.
Problem/Task
As you know by now, Zope 3 gains its incredible stability from testing any code in great detail. The
currently most common method is to write unit tests. This chapter introduces unit tests \xe2\x80\x93 which
are Zope 3 independent \xe2\x80\x93 and introduces some of the subtleties.
Solution
44.1
Implementing the Sample Class
Before we can write tests, we have to write some code that we can test. Here, we will implement
a simple class called Sample with a public attribute title and description that is accessed
via getDescription() and mutated using setDescription(). Further, the description must be
either a regular or unicode string.
Since this code will not depend on Zope, open a \xef\xac\x81le named test sample.py anywhere and add
the following class:
1 Sample(object):
2
"""A trivial Sample object."""
3
4
title = None
5
6
def __init__(self):
7
"""Initialize object."""
8
self._description = \xe2\x80\x99\xe2\x80\x99
9
1

2
CHAPTER 44. WRITING BASIC UNIT TESTS
10
def setDescription(self, value):
11
"""Change the value of the description."""
12
assert isinstance(value, (str, unicode))
13
self._description = value
14
15
def getDescription(self):
16
"""Change the value of the description."""
17
return self._description
Line 4: The title is just publicly declared and a value of None is given. Therefore this is just
a regular attribute.
Line 8: The actual description string will be stored in description.
Line 12: Make sure that the description is only a regular or unicode string, like it was stated in
the requirements.
If you wish you can now manually test the class with the interactive Python shell. Just start
Python by entering python in your shell prompt. Note that you should be in the directory in
which test sample.py is located when starting Python (an alternative is of course to specify the
directory in your PYTHONPATH.)
1 >>> from test_sample import Sample
2 >>> sample = Sample()
3 >>> print sample.title
4 None
5 >>> sample.title = \xe2\x80\x99Title\xe2\x80\x99
6 >>> print sample.title
7 Title
8 >>> print sample.getDescription()
9
10 >>> sample.setDescription(\xe2\x80\x99Hello World\xe2\x80\x99)
11 >>> print sample.getDescription()
12 Hello World
13 >>> sample.setDescription(None)
14 Traceback (most recent call last):
15
File "<stdin>", line 1, in ?
16
File "test_sample.py", line 31, in setDescription
17
assert isinstance(value, (str, unicode))
18 AssertionError
As you can see in the last test, non-string object types are not allowed as descriptions and an
AssertionError is raised.
44.2
Writing the Unit Tests
The goal of writing the unit tests is to convert this informal, manual, and interactive testing session
into a formal test class. Python provides already a module called unittest for this purpose, which
is a port of the Java-based unit testing product, JUnit, by Kent Beck and Erich Gamma. There are
three levels to the testing framework (this list deviates a bit from the original de\xef\xac\x81nitions as found
in the Python library documentation. 1).
1 http://www.python.org/doc/current/lib/module-unittest.html

44.2. WRITING THE UNIT TESTS
3
The smallest unit is obviously the \xe2\x80\x9ctest\xe2\x80\x9d, which is a single method in a TestCase class that
tests the behavior of a small piece of code or a particular aspect of an implementation. The \xe2\x80\x9ctest
case\xe2\x80\x9d is then a collection tests that share the same setup/inputs. On top of all of this sits the \xe2\x80\x9ctest
suite\xe2\x80\x9d which is a collection of test cases and/or other test suites. Test suites combine tests that
should be executed together. With the correct setup (as shown in the example below), you can
then execute test suites. For large projects like Zope 3, it is useful to know that there is also the
concept of a test runner, which manages the test run of all or a set of tests. The runner provides
useful feedback to the application, so that various user interfaces can be developed on top of it.
But enough about the theory. In the following example, which you can simply put into the same
\xef\xac\x81le as your code above, you will see a test in common Zope 3 style.
1 import unittest
2
3 class SampleTest(unittest.TestCase):
4
"""Test the Sample class"""
5
6
def test_title(self):
7
sample = Sample()
8
self.assertEqual(sample.title, None)
9
sample.title = \xe2\x80\x99Sample Title\xe2\x80\x99
10
self.assertEqual(sample.title, \xe2\x80\x99Sample Title\xe2\x80\x99)
11
12
def test_getDescription(self):
13
sample = Sample()
14
self.assertEqual(sample.getDescription(), \xe2\x80\x99\xe2\x80\x99)
15
sample._description = "Description"
16
self.assertEqual(sample.getDescription(), \xe2\x80\x99Description\xe2\x80\x99)
17
18
def test_setDescription(self):
19
sample = Sample()
20
self.assertEqual(sample._description, \xe2\x80\x99\xe2\x80\x99)
21
sample.setDescription(\xe2\x80\x99Description\xe2\x80\x99)
22
self.assertEqual(sample._description, \xe2\x80\x99Description\xe2\x80\x99)
23
sample.setDescription(u\xe2\x80\x99Description2\xe2\x80\x99)
24
self.assertEqual(sample._description, u\xe2\x80\x99Description2\xe2\x80\x99)
25
self.assertRaises(AssertionError, sample.setDescription, None)
26
27
28 def test_suite():
29
return unittest.TestSuite((
30
unittest.defaultTestLoader.loadTestsFromTestCase(SampleTest),
31
))
32
33 if __name__ == \xe2\x80\x99__main__\xe2\x80\x99:
34
unittest.main(defaultTest=\xe2\x80\x99test_suite\xe2\x80\x99)
Line 3\xe2\x80\x934: We usually develop test classes which must inherit from TestCase. While often not
done, it is a good idea to give the class a meaningful docstring that describes the purpose of the
tests it includes.
Line 6, 12 & 18: When a test case is run, a method called runTests() is executed. While it
is possible to override this method to run tests di\xef\xac\x80erently, the default option will look for any
method whose name starts with test and execute it as a single test. This way we can create
a \xe2\x80\x9ctest method\xe2\x80\x9d for each aspect, method, function or property of the code to be tested. This
default is very sensible and is used everywhere in Zope 3.

4
CHAPTER 44. WRITING BASIC UNIT TESTS
Note that there is no docstring for test methods. This is intentional. If a docstring is speci\xef\xac\x81ed,
it is used instead of the method name to identify the test. When specifying a docstring, we have
noticed that it is very di\xef\xac\x83cult to identify the test later; therefore the method name is a much
better choice.
Line 8, 10, 14, . . . : The TestCase class implements a handful of methods that aid you with the
testing. Here are some of the most frequently used ones. For a complete list see the standard
Python documentation referenced above.
\xe2\x80\xa2 assertEqual(first,second[,msg])
Checks whether the first and second value are equal. If the test fails, the msg or None
is returned.
\xe2\x80\xa2 assertNotEqual(first,second[,msg])
This is simply the opposite to assertEqual() by checking for non-equality.
\xe2\x80\xa2 assertRaises(exception,callable,...)
You expect the callable to raise exception when executed. After the callable you can
specify any amount of positional and keyword arguments for the callable. If you expect
a group of exceptions from the execution, you can make exception a tuple of possible
exceptions.
\xe2\x80\xa2 assert (expr[,msg])
Assert checks whether the speci\xef\xac\x81ed expression executes correctly. If not, the test fails and
msg or None is returned.
\xe2\x80\xa2 assertEqual()
This testing method is equivalent to assertEqual().
\xe2\x80\xa2 assertTrue(expr[,msg])
This method is equivalent to assert (expr[,msg]).
\xe2\x80\xa2 assertFalse()
This is the opposite to assertTrue().
\xe2\x80\xa2 fail([msg])
Fails the running test without any evaluation. This is commonly used when testing various
possible execution paths at once and you would like to signify a failure if an improper path
was taken.
Line 6\xe2\x80\x9310: This method tests the title attribute of the Sample class. The \xef\xac\x81rst test should
be of course that the attribute exists and has the expected initial value (line 8). Then the title
attribute is changed and we check whether the value was really stored. This might seem like
overkill, but later you might change the title in a way that it uses properties instead. Then it
becomes very important to check whether this test still passes.
Line 12\xe2\x80\x9316: First we simply check that getDescription() returns the correct default value.
Since we do not want to use other API calls like setDescription() we set a new value of the
description via the implementation-internal description attribute (line 15). This is okay! Unit
tests can make use of implementation-speci\xef\xac\x81c attributes and methods. Finally we just check that
the correct value is returned.

44.3. RUNNING THE TESTS
5
Line 18\xe2\x80\x9325: On line 21\xe2\x80\x9324 it is checked that both regular and unicode strings are set correctly.
In the last line of the test we make sure that no other type of objects can be set as a description
and that an error is raised.
28\xe2\x80\x9331: This method returns a test suite that includes all test cases created in this module. It is
used by the Zope 3 test runner when it picks up all available tests. You would basically add the
line unittest.defaultTestLoader.loadTestsFromTestCase(TestCaseClass) for each additional test case.
33\xe2\x80\x9334: In order to make the test module runnable by itself, you can execute unittest.main()
when the module is run.
44.3
Running the Tests
You can run the test by simply calling pythontest sample.py from the directory you saved the
\xef\xac\x81le in. Here is the result you should see:
.
--------------------------------------------------------------------
n 3 tests in 0.001s
The three dots represent the three tests that were run. If a test had failed, it would have been
reported pointing out the failing test and providing a small traceback.
When using the default Zope 3 test runner, tests will be picked up as long as they follow some
conventions.
\xe2\x80\xa2 The tests must either be in a package or be a module called tests.
\xe2\x80\xa2 If tests is a package, then all test modules inside must also have a name starting with test,
as it is the case with our name test sample.py.
\xe2\x80\xa2 The test module must be somewhere in the Zope 3 source tree, since the test runner looks
only for \xef\xac\x81les there.
In our case, you could simply create a tests package in ZOPE3/src (do not forget the
init .
py \xef\xac\x81le). Then place the test sample.py \xef\xac\x81le into this directory.
You you can use the test runner to run only the sample tests as follows from the Zope 3 root
directory:
python test.py -vp tests.test_sample
The -v option stands for verbose mode, so that detailed information about a test failure is
provided. The -p option enables a progress bar that tells you how many tests out of all have been
completed. There are many more options that can be speci\xef\xac\x81ed. You can get a full list of them with
the option -h: pythontest.py-h.
The output of the call above is as follows:
nfiguration file found.
nning UNIT tests at level 1
nning UNIT tests from /opt/zope/Zope3
3/3 (100.0%): test_title (tests.test_sample.SampleTest)
--------------------------------------------------------------------
n 3 tests in 0.002s

6
CHAPTER 44. WRITING BASIC UNIT TESTS
nning FUNCTIONAL tests at level 1
nning FUNCTIONAL tests from /opt/zope/Zope3
--------------------------------------------------------------------
n 0 tests in 0.000s
Line 1: The test runner uses a con\xef\xac\x81guration \xef\xac\x81le for some setup. This allows developers to use
the test runner for other projects as well. This message simply tells us that the con\xef\xac\x81guration \xef\xac\x81le
was found.
Line 2\xe2\x80\x938: The unit tests are run. On line 4 you can see the progress bar.
Line 9\xe2\x80\x9315: The functional tests are run, since the default test runner runs both types of tests.
Since we do not have any functional tests in the speci\xef\xac\x81ed module, there are no tests to run. To
just run the unit tests, use option -u and -f for just running the functional tests. See \xe2\x80\x9cWriting
Functional Tests\xe2\x80\x9d for more details on functional tests.

44.3. RUNNING THE TESTS
7
Exercises
1. It is not very common to do the setup \xe2\x80\x93 in our case sample=Sample() \xe2\x80\x93 in every test
method. Instead there exists a method called setUp() and its counterpart tearDown that
are run before and after each test, respectively. Change the test code above, so that it uses
the setUp() method. In later chapters and the rest of the book we will frequently use this
method of setting up tests.
2. Currently the test setDescription() test only veri\xef\xac\x81es that None is not allowed as input
value.
(a) Improve the test, so that all other builtin types are tested as well.
(b) Also, make sure that any objects inheriting from str or unicode pass as valid values.

\ndiff --git a/Products/PortalTransforms/tests/output/demo1.html.nofilename b/Products/PortalTransforms/tests/output/demo1.html.nofilename\nindex 2f60f41..5457311 100644\n--- a/Products/PortalTransforms/tests/output/demo1.html.nofilename\n+++ b/Products/PortalTransforms/tests/output/demo1.html.nofilename\n@@ -1 +1 @@\n-Chapter 44
Writing Basic Unit Tests
Di\xef\xac\x83culty
Newcomer
Skills
\xe2\x80\xa2 All you need to know is some Python.
Problem/Task
As you know by now, Zope 3 gains its incredible stability from testing any code in great detail. The
currently most common method is to write unit tests. This chapter introduces unit tests \xe2\x80\x93 which
are Zope 3 independent \xe2\x80\x93 and introduces some of the subtleties.
Solution
44.1
Implementing the Sample Class
Before we can write tests, we have to write some code that we can test. Here, we will implement
a simple class called Sample with a public attribute title and description that is accessed
via getDescription() and mutated using setDescription(). Further, the description must be
either a regular or unicode string.
Since this code will not depend on Zope, open a \xef\xac\x81le named test sample.py anywhere and add
the following class:
1 Sample(object):
2
"""A trivial Sample object."""
3
4
title = None
5
6
def __init__(self):
7
"""Initialize object."""
8
self._description = \xe2\x80\x99\xe2\x80\x99
9
1

2
CHAPTER 44. WRITING BASIC UNIT TESTS
10
def setDescription(self, value):
11
"""Change the value of the description."""
12
assert isinstance(value, (str, unicode))
13
self._description = value
14
15
def getDescription(self):
16
"""Change the value of the description."""
17
return self._description
Line 4: The title is just publicly declared and a value of None is given. Therefore this is just
a regular attribute.
Line 8: The actual description string will be stored in description.
Line 12: Make sure that the description is only a regular or unicode string, like it was stated in
the requirements.
If you wish you can now manually test the class with the interactive Python shell. Just start
Python by entering python in your shell prompt. Note that you should be in the directory in
which test sample.py is located when starting Python (an alternative is of course to specify the
directory in your PYTHONPATH.)
1 >>> from test_sample import Sample
2 >>> sample = Sample()
3 >>> print sample.title
4 None
5 >>> sample.title = \xe2\x80\x99Title\xe2\x80\x99
6 >>> print sample.title
7 Title
8 >>> print sample.getDescription()
9
10 >>> sample.setDescription(\xe2\x80\x99Hello World\xe2\x80\x99)
11 >>> print sample.getDescription()
12 Hello World
13 >>> sample.setDescription(None)
14 Traceback (most recent call last):
15
File "<stdin>", line 1, in ?
16
File "test_sample.py", line 31, in setDescription
17
assert isinstance(value, (str, unicode))
18 AssertionError
As you can see in the last test, non-string object types are not allowed as descriptions and an
AssertionError is raised.
44.2
Writing the Unit Tests
The goal of writing the unit tests is to convert this informal, manual, and interactive testing session
into a formal test class. Python provides already a module called unittest for this purpose, which
is a port of the Java-based unit testing product, JUnit, by Kent Beck and Erich Gamma. There are
three levels to the testing framework (this list deviates a bit from the original de\xef\xac\x81nitions as found
in the Python library documentation. 1).
1 http://www.python.org/doc/current/lib/module-unittest.html

44.2. WRITING THE UNIT TESTS
3
The smallest unit is obviously the \xe2\x80\x9ctest\xe2\x80\x9d, which is a single method in a TestCase class that
tests the behavior of a small piece of code or a particular aspect of an implementation. The \xe2\x80\x9ctest
case\xe2\x80\x9d is then a collection tests that share the same setup/inputs. On top of all of this sits the \xe2\x80\x9ctest
suite\xe2\x80\x9d which is a collection of test cases and/or other test suites. Test suites combine tests that
should be executed together. With the correct setup (as shown in the example below), you can
then execute test suites. For large projects like Zope 3, it is useful to know that there is also the
concept of a test runner, which manages the test run of all or a set of tests. The runner provides
useful feedback to the application, so that various user interfaces can be developed on top of it.
But enough about the theory. In the following example, which you can simply put into the same
\xef\xac\x81le as your code above, you will see a test in common Zope 3 style.
1 import unittest
2
3 class SampleTest(unittest.TestCase):
4
"""Test the Sample class"""
5
6
def test_title(self):
7
sample = Sample()
8
self.assertEqual(sample.title, None)
9
sample.title = \xe2\x80\x99Sample Title\xe2\x80\x99
10
self.assertEqual(sample.title, \xe2\x80\x99Sample Title\xe2\x80\x99)
11
12
def test_getDescription(self):
13
sample = Sample()
14
self.assertEqual(sample.getDescription(), \xe2\x80\x99\xe2\x80\x99)
15
sample._description = "Description"
16
self.assertEqual(sample.getDescription(), \xe2\x80\x99Description\xe2\x80\x99)
17
18
def test_setDescription(self):
19
sample = Sample()
20
self.assertEqual(sample._description, \xe2\x80\x99\xe2\x80\x99)
21
sample.setDescription(\xe2\x80\x99Description\xe2\x80\x99)
22
self.assertEqual(sample._description, \xe2\x80\x99Description\xe2\x80\x99)
23
sample.setDescription(u\xe2\x80\x99Description2\xe2\x80\x99)
24
self.assertEqual(sample._description, u\xe2\x80\x99Description2\xe2\x80\x99)
25
self.assertRaises(AssertionError, sample.setDescription, None)
26
27
28 def test_suite():
29
return unittest.TestSuite((
30
unittest.makeSuite(SampleTest),
31
))
32
33 if __name__ == \xe2\x80\x99__main__\xe2\x80\x99:
34
unittest.main(defaultTest=\xe2\x80\x99test_suite\xe2\x80\x99)
Line 3\xe2\x80\x934: We usually develop test classes which must inherit from TestCase. While often not
done, it is a good idea to give the class a meaningful docstring that describes the purpose of the
tests it includes.
Line 6, 12 & 18: When a test case is run, a method called runTests() is executed. While it
is possible to override this method to run tests di\xef\xac\x80erently, the default option will look for any
method whose name starts with test and execute it as a single test. This way we can create
a \xe2\x80\x9ctest method\xe2\x80\x9d for each aspect, method, function or property of the code to be tested. This
default is very sensible and is used everywhere in Zope 3.

4
CHAPTER 44. WRITING BASIC UNIT TESTS
Note that there is no docstring for test methods. This is intentional. If a docstring is speci\xef\xac\x81ed,
it is used instead of the method name to identify the test. When specifying a docstring, we have
noticed that it is very di\xef\xac\x83cult to identify the test later; therefore the method name is a much
better choice.
Line 8, 10, 14, . . . : The TestCase class implements a handful of methods that aid you with the
testing. Here are some of the most frequently used ones. For a complete list see the standard
Python documentation referenced above.
\xe2\x80\xa2 assertEqual(first,second[,msg])
Checks whether the first and second value are equal. If the test fails, the msg or None
is returned.
\xe2\x80\xa2 assertNotEqual(first,second[,msg])
This is simply the opposite to assertEqual() by checking for non-equality.
\xe2\x80\xa2 assertRaises(exception,callable,...)
You expect the callable to raise exception when executed. After the callable you can
specify any amount of positional and keyword arguments for the callable. If you expect
a group of exceptions from the execution, you can make exception a tuple of possible
exceptions.
\xe2\x80\xa2 assert (expr[,msg])
Assert checks whether the speci\xef\xac\x81ed expression executes correctly. If not, the test fails and
msg or None is returned.
\xe2\x80\xa2 assertEqual()
This testing method is equivalent to assertEqual().
\xe2\x80\xa2 assertTrue(expr[,msg])
This method is equivalent to assert (expr[,msg]).
\xe2\x80\xa2 assertFalse()
This is the opposite to assertTrue().
\xe2\x80\xa2 fail([msg])
Fails the running test without any evaluation. This is commonly used when testing various
possible execution paths at once and you would like to signify a failure if an improper path
was taken.
Line 6\xe2\x80\x9310: This method tests the title attribute of the Sample class. The \xef\xac\x81rst test should
be of course that the attribute exists and has the expected initial value (line 8). Then the title
attribute is changed and we check whether the value was really stored. This might seem like
overkill, but later you might change the title in a way that it uses properties instead. Then it
becomes very important to check whether this test still passes.
Line 12\xe2\x80\x9316: First we simply check that getDescription() returns the correct default value.
Since we do not want to use other API calls like setDescription() we set a new value of the
description via the implementation-internal description attribute (line 15). This is okay! Unit
tests can make use of implementation-speci\xef\xac\x81c attributes and methods. Finally we just check that
the correct value is returned.

44.3. RUNNING THE TESTS
5
Line 18\xe2\x80\x9325: On line 21\xe2\x80\x9324 it is checked that both regular and unicode strings are set correctly.
In the last line of the test we make sure that no other type of objects can be set as a description
and that an error is raised.
28\xe2\x80\x9331: This method returns a test suite that includes all test cases created in this module. It is
used by the Zope 3 test runner when it picks up all available tests. You would basically add the
line unittest.makeSuite(TestCaseClass) for each additional test case.
33\xe2\x80\x9334: In order to make the test module runnable by itself, you can execute unittest.main()
when the module is run.
44.3
Running the Tests
You can run the test by simply calling pythontest sample.py from the directory you saved the
\xef\xac\x81le in. Here is the result you should see:
.
--------------------------------------------------------------------
n 3 tests in 0.001s
The three dots represent the three tests that were run. If a test had failed, it would have been
reported pointing out the failing test and providing a small traceback.
When using the default Zope 3 test runner, tests will be picked up as long as they follow some
conventions.
\xe2\x80\xa2 The tests must either be in a package or be a module called tests.
\xe2\x80\xa2 If tests is a package, then all test modules inside must also have a name starting with test,
as it is the case with our name test sample.py.
\xe2\x80\xa2 The test module must be somewhere in the Zope 3 source tree, since the test runner looks
only for \xef\xac\x81les there.
In our case, you could simply create a tests package in ZOPE3/src (do not forget the
init .
py \xef\xac\x81le). Then place the test sample.py \xef\xac\x81le into this directory.
You you can use the test runner to run only the sample tests as follows from the Zope 3 root
directory:
python test.py -vp tests.test_sample
The -v option stands for verbose mode, so that detailed information about a test failure is
provided. The -p option enables a progress bar that tells you how many tests out of all have been
completed. There are many more options that can be speci\xef\xac\x81ed. You can get a full list of them with
the option -h: pythontest.py-h.
The output of the call above is as follows:
nfiguration file found.
nning UNIT tests at level 1
nning UNIT tests from /opt/zope/Zope3
3/3 (100.0%): test_title (tests.test_sample.SampleTest)
--------------------------------------------------------------------
n 3 tests in 0.002s

6
CHAPTER 44. WRITING BASIC UNIT TESTS
nning FUNCTIONAL tests at level 1
nning FUNCTIONAL tests from /opt/zope/Zope3
--------------------------------------------------------------------
n 0 tests in 0.000s
Line 1: The test runner uses a con\xef\xac\x81guration \xef\xac\x81le for some setup. This allows developers to use
the test runner for other projects as well. This message simply tells us that the con\xef\xac\x81guration \xef\xac\x81le
was found.
Line 2\xe2\x80\x938: The unit tests are run. On line 4 you can see the progress bar.
Line 9\xe2\x80\x9315: The functional tests are run, since the default test runner runs both types of tests.
Since we do not have any functional tests in the speci\xef\xac\x81ed module, there are no tests to run. To
just run the unit tests, use option -u and -f for just running the functional tests. See \xe2\x80\x9cWriting
Functional Tests\xe2\x80\x9d for more details on functional tests.

44.3. RUNNING THE TESTS
7
Exercises
1. It is not very common to do the setup \xe2\x80\x93 in our case sample=Sample() \xe2\x80\x93 in every test
method. Instead there exists a method called setUp() and its counterpart tearDown that
are run before and after each test, respectively. Change the test code above, so that it uses
the setUp() method. In later chapters and the rest of the book we will frequently use this
method of setting up tests.
2. Currently the test setDescription() test only veri\xef\xac\x81es that None is not allowed as input
value.
(a) Improve the test, so that all other builtin types are tested as well.
(b) Also, make sure that any objects inheriting from str or unicode pass as valid values.

\n+Chapter 44
Writing Basic Unit Tests
Di\xef\xac\x83culty
Newcomer
Skills
\xe2\x80\xa2 All you need to know is some Python.
Problem/Task
As you know by now, Zope 3 gains its incredible stability from testing any code in great detail. The
currently most common method is to write unit tests. This chapter introduces unit tests \xe2\x80\x93 which
are Zope 3 independent \xe2\x80\x93 and introduces some of the subtleties.
Solution
44.1
Implementing the Sample Class
Before we can write tests, we have to write some code that we can test. Here, we will implement
a simple class called Sample with a public attribute title and description that is accessed
via getDescription() and mutated using setDescription(). Further, the description must be
either a regular or unicode string.
Since this code will not depend on Zope, open a \xef\xac\x81le named test sample.py anywhere and add
the following class:
1 Sample(object):
2
"""A trivial Sample object."""
3
4
title = None
5
6
def __init__(self):
7
"""Initialize object."""
8
self._description = \xe2\x80\x99\xe2\x80\x99
9
1

2
CHAPTER 44. WRITING BASIC UNIT TESTS
10
def setDescription(self, value):
11
"""Change the value of the description."""
12
assert isinstance(value, (str, unicode))
13
self._description = value
14
15
def getDescription(self):
16
"""Change the value of the description."""
17
return self._description
Line 4: The title is just publicly declared and a value of None is given. Therefore this is just
a regular attribute.
Line 8: The actual description string will be stored in description.
Line 12: Make sure that the description is only a regular or unicode string, like it was stated in
the requirements.
If you wish you can now manually test the class with the interactive Python shell. Just start
Python by entering python in your shell prompt. Note that you should be in the directory in
which test sample.py is located when starting Python (an alternative is of course to specify the
directory in your PYTHONPATH.)
1 >>> from test_sample import Sample
2 >>> sample = Sample()
3 >>> print sample.title
4 None
5 >>> sample.title = \xe2\x80\x99Title\xe2\x80\x99
6 >>> print sample.title
7 Title
8 >>> print sample.getDescription()
9
10 >>> sample.setDescription(\xe2\x80\x99Hello World\xe2\x80\x99)
11 >>> print sample.getDescription()
12 Hello World
13 >>> sample.setDescription(None)
14 Traceback (most recent call last):
15
File "<stdin>", line 1, in ?
16
File "test_sample.py", line 31, in setDescription
17
assert isinstance(value, (str, unicode))
18 AssertionError
As you can see in the last test, non-string object types are not allowed as descriptions and an
AssertionError is raised.
44.2
Writing the Unit Tests
The goal of writing the unit tests is to convert this informal, manual, and interactive testing session
into a formal test class. Python provides already a module called unittest for this purpose, which
is a port of the Java-based unit testing product, JUnit, by Kent Beck and Erich Gamma. There are
three levels to the testing framework (this list deviates a bit from the original de\xef\xac\x81nitions as found
in the Python library documentation. 1).
1 http://www.python.org/doc/current/lib/module-unittest.html

44.2. WRITING THE UNIT TESTS
3
The smallest unit is obviously the \xe2\x80\x9ctest\xe2\x80\x9d, which is a single method in a TestCase class that
tests the behavior of a small piece of code or a particular aspect of an implementation. The \xe2\x80\x9ctest
case\xe2\x80\x9d is then a collection tests that share the same setup/inputs. On top of all of this sits the \xe2\x80\x9ctest
suite\xe2\x80\x9d which is a collection of test cases and/or other test suites. Test suites combine tests that
should be executed together. With the correct setup (as shown in the example below), you can
then execute test suites. For large projects like Zope 3, it is useful to know that there is also the
concept of a test runner, which manages the test run of all or a set of tests. The runner provides
useful feedback to the application, so that various user interfaces can be developed on top of it.
But enough about the theory. In the following example, which you can simply put into the same
\xef\xac\x81le as your code above, you will see a test in common Zope 3 style.
1 import unittest
2
3 class SampleTest(unittest.TestCase):
4
"""Test the Sample class"""
5
6
def test_title(self):
7
sample = Sample()
8
self.assertEqual(sample.title, None)
9
sample.title = \xe2\x80\x99Sample Title\xe2\x80\x99
10
self.assertEqual(sample.title, \xe2\x80\x99Sample Title\xe2\x80\x99)
11
12
def test_getDescription(self):
13
sample = Sample()
14
self.assertEqual(sample.getDescription(), \xe2\x80\x99\xe2\x80\x99)
15
sample._description = "Description"
16
self.assertEqual(sample.getDescription(), \xe2\x80\x99Description\xe2\x80\x99)
17
18
def test_setDescription(self):
19
sample = Sample()
20
self.assertEqual(sample._description, \xe2\x80\x99\xe2\x80\x99)
21
sample.setDescription(\xe2\x80\x99Description\xe2\x80\x99)
22
self.assertEqual(sample._description, \xe2\x80\x99Description\xe2\x80\x99)
23
sample.setDescription(u\xe2\x80\x99Description2\xe2\x80\x99)
24
self.assertEqual(sample._description, u\xe2\x80\x99Description2\xe2\x80\x99)
25
self.assertRaises(AssertionError, sample.setDescription, None)
26
27
28 def test_suite():
29
return unittest.TestSuite((
30
unittest.defaultTestLoader.loadTestsFromTestCase(SampleTest),
31
))
32
33 if __name__ == \xe2\x80\x99__main__\xe2\x80\x99:
34
unittest.main(defaultTest=\xe2\x80\x99test_suite\xe2\x80\x99)
Line 3\xe2\x80\x934: We usually develop test classes which must inherit from TestCase. While often not
done, it is a good idea to give the class a meaningful docstring that describes the purpose of the
tests it includes.
Line 6, 12 & 18: When a test case is run, a method called runTests() is executed. While it
is possible to override this method to run tests di\xef\xac\x80erently, the default option will look for any
method whose name starts with test and execute it as a single test. This way we can create
a \xe2\x80\x9ctest method\xe2\x80\x9d for each aspect, method, function or property of the code to be tested. This
default is very sensible and is used everywhere in Zope 3.

4
CHAPTER 44. WRITING BASIC UNIT TESTS
Note that there is no docstring for test methods. This is intentional. If a docstring is speci\xef\xac\x81ed,
it is used instead of the method name to identify the test. When specifying a docstring, we have
noticed that it is very di\xef\xac\x83cult to identify the test later; therefore the method name is a much
better choice.
Line 8, 10, 14, . . . : The TestCase class implements a handful of methods that aid you with the
testing. Here are some of the most frequently used ones. For a complete list see the standard
Python documentation referenced above.
\xe2\x80\xa2 assertEqual(first,second[,msg])
Checks whether the first and second value are equal. If the test fails, the msg or None
is returned.
\xe2\x80\xa2 assertNotEqual(first,second[,msg])
This is simply the opposite to assertEqual() by checking for non-equality.
\xe2\x80\xa2 assertRaises(exception,callable,...)
You expect the callable to raise exception when executed. After the callable you can
specify any amount of positional and keyword arguments for the callable. If you expect
a group of exceptions from the execution, you can make exception a tuple of possible
exceptions.
\xe2\x80\xa2 assert (expr[,msg])
Assert checks whether the speci\xef\xac\x81ed expression executes correctly. If not, the test fails and
msg or None is returned.
\xe2\x80\xa2 assertEqual()
This testing method is equivalent to assertEqual().
\xe2\x80\xa2 assertTrue(expr[,msg])
This method is equivalent to assert (expr[,msg]).
\xe2\x80\xa2 assertFalse()
This is the opposite to assertTrue().
\xe2\x80\xa2 fail([msg])
Fails the running test without any evaluation. This is commonly used when testing various
possible execution paths at once and you would like to signify a failure if an improper path
was taken.
Line 6\xe2\x80\x9310: This method tests the title attribute of the Sample class. The \xef\xac\x81rst test should
be of course that the attribute exists and has the expected initial value (line 8). Then the title
attribute is changed and we check whether the value was really stored. This might seem like
overkill, but later you might change the title in a way that it uses properties instead. Then it
becomes very important to check whether this test still passes.
Line 12\xe2\x80\x9316: First we simply check that getDescription() returns the correct default value.
Since we do not want to use other API calls like setDescription() we set a new value of the
description via the implementation-internal description attribute (line 15). This is okay! Unit
tests can make use of implementation-speci\xef\xac\x81c attributes and methods. Finally we just check that
the correct value is returned.

44.3. RUNNING THE TESTS
5
Line 18\xe2\x80\x9325: On line 21\xe2\x80\x9324 it is checked that both regular and unicode strings are set correctly.
In the last line of the test we make sure that no other type of objects can be set as a description
and that an error is raised.
28\xe2\x80\x9331: This method returns a test suite that includes all test cases created in this module. It is
used by the Zope 3 test runner when it picks up all available tests. You would basically add the
line unittest.defaultTestLoader.loadTestsFromTestCase(TestCaseClass) for each additional test case.
33\xe2\x80\x9334: In order to make the test module runnable by itself, you can execute unittest.main()
when the module is run.
44.3
Running the Tests
You can run the test by simply calling pythontest sample.py from the directory you saved the
\xef\xac\x81le in. Here is the result you should see:
.
--------------------------------------------------------------------
n 3 tests in 0.001s
The three dots represent the three tests that were run. If a test had failed, it would have been
reported pointing out the failing test and providing a small traceback.
When using the default Zope 3 test runner, tests will be picked up as long as they follow some
conventions.
\xe2\x80\xa2 The tests must either be in a package or be a module called tests.
\xe2\x80\xa2 If tests is a package, then all test modules inside must also have a name starting with test,
as it is the case with our name test sample.py.
\xe2\x80\xa2 The test module must be somewhere in the Zope 3 source tree, since the test runner looks
only for \xef\xac\x81les there.
In our case, you could simply create a tests package in ZOPE3/src (do not forget the
init .
py \xef\xac\x81le). Then place the test sample.py \xef\xac\x81le into this directory.
You you can use the test runner to run only the sample tests as follows from the Zope 3 root
directory:
python test.py -vp tests.test_sample
The -v option stands for verbose mode, so that detailed information about a test failure is
provided. The -p option enables a progress bar that tells you how many tests out of all have been
completed. There are many more options that can be speci\xef\xac\x81ed. You can get a full list of them with
the option -h: pythontest.py-h.
The output of the call above is as follows:
nfiguration file found.
nning UNIT tests at level 1
nning UNIT tests from /opt/zope/Zope3
3/3 (100.0%): test_title (tests.test_sample.SampleTest)
--------------------------------------------------------------------
n 3 tests in 0.002s

6
CHAPTER 44. WRITING BASIC UNIT TESTS
nning FUNCTIONAL tests at level 1
nning FUNCTIONAL tests from /opt/zope/Zope3
--------------------------------------------------------------------
n 0 tests in 0.000s
Line 1: The test runner uses a con\xef\xac\x81guration \xef\xac\x81le for some setup. This allows developers to use
the test runner for other projects as well. This message simply tells us that the con\xef\xac\x81guration \xef\xac\x81le
was found.
Line 2\xe2\x80\x938: The unit tests are run. On line 4 you can see the progress bar.
Line 9\xe2\x80\x9315: The functional tests are run, since the default test runner runs both types of tests.
Since we do not have any functional tests in the speci\xef\xac\x81ed module, there are no tests to run. To
just run the unit tests, use option -u and -f for just running the functional tests. See \xe2\x80\x9cWriting
Functional Tests\xe2\x80\x9d for more details on functional tests.

44.3. RUNNING THE TESTS
7
Exercises
1. It is not very common to do the setup \xe2\x80\x93 in our case sample=Sample() \xe2\x80\x93 in every test
method. Instead there exists a method called setUp() and its counterpart tearDown that
are run before and after each test, respectively. Change the test code above, so that it uses
the setUp() method. In later chapters and the rest of the book we will frequently use this
method of setting up tests.
2. Currently the test setDescription() test only veri\xef\xac\x81es that None is not allowed as input
value.
(a) Improve the test, so that all other builtin types are tested as well.
(b) Also, make sure that any objects inheriting from str or unicode pass as valid values.

\ndiff --git a/Products/PortalTransforms/tests/test_intelligenttext.py b/Products/PortalTransforms/tests/test_intelligenttext.py\nindex 77ddb53..f25f9f5 100644\n--- a/Products/PortalTransforms/tests/test_intelligenttext.py\n+++ b/Products/PortalTransforms/tests/test_intelligenttext.py\n@@ -155,10 +155,9 @@ def testWhitespace(self):\n \n \n def test_suite():\n- from unittest import makeSuite\n- from unittest import TestSuite\n+ import unittest\n \n- suite = TestSuite()\n- suite.addTest(makeSuite(TestIntelligentTextToHtml))\n- suite.addTest(makeSuite(TestHtmlToIntelligentText))\n- return suite\n+ return unittest.TestSuite((\n+ unittest.defaultTestLoader.loadTestsFromTestCase(TestIntelligentTextToHtml),\n+ unittest.defaultTestLoader.loadTestsFromTestCase(TestHtmlToIntelligentText),\n+ ))\ndiff --git a/Products/PortalTransforms/tests/test_transforms.py b/Products/PortalTransforms/tests/test_transforms.py\nindex e8c24e5..08d135a 100644\n--- a/Products/PortalTransforms/tests/test_transforms.py\n+++ b/Products/PortalTransforms/tests/test_transforms.py\n@@ -714,10 +714,9 @@ class TransformTestSubclass(TransformTest):\n \n \n def test_suite():\n- from unittest import makeSuite\n- from unittest import TestSuite\n+ import unittest\n \n- suite = TestSuite()\n+ suite = unittest.TestSuite()\n for test in make_tests():\n- suite.addTest(makeSuite(test))\n+ suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(test))\n return suite\ndiff --git a/docs/dev_manual.rst b/docs/dev_manual.rst\nindex be8e716..11d0c05 100644\n--- a/docs/dev_manual.rst\n+++ b/docs/dev_manual.rst\n@@ -92,7 +92,7 @@ Writing a new transformation\n ============================\n Writing a new transform should be an easy task. You only have to follow a\n simple interface to do it, but knowing some advanced features and provided\n-utilities may help to do it quicker... \n+utilities may help to do it quicker...\n \n \n Related interfaces\n@@ -182,16 +182,16 @@ Transform utilities may be found in the libtransforms subpackage. You\'ll find\n there the following modules :\n \n *commandtransform*\n- provides a base class for external command based transforms. \n+ provides a base class for external command based transforms.\n \n *retransform*\n- provides a base class for regular expression based transforms. \n+ provides a base class for regular expression based transforms.\n \n *html4zope*\n provides a docutils HTML writer for Zope.\n \n *utils*\n- provides some utilities functions. \n+ provides some utilities functions.\n \n \n Write a test your transform!\n@@ -202,11 +202,11 @@ cola into beer (I let you find MIME type for these content types ;). Basically,\n your test file should be::\n \n from test_transforms import make_tests\n- \n+\n tests =(\'Products.MyTransforms.colabeer\', "drink.cola", "drink.beer", None, 0)\n \n def test_suite():\n- return TestSuite([makeSuite(test) for test in make_tests()])\n+ return TestSuite([unittest.defaultTestLoader.loadTestsFromTestCase(test) for test in make_tests()])\n \n if __name__==\'__main__\':\n main(defaultTest=\'test_suite\')\n@@ -216,10 +216,10 @@ your test file should be::\n In this example:\n \n - "Products.MyTransforms.colabeer" is the module defining your transform (you\n- can also give directly the transform instance). \n+ can also give directly the transform instance).\n \n - "drink.cola" is the name of the file containing data to give to your transform\n- as input. \n+ as input.\n \n - "drink.beer" is the file containing expected transform result (what the getData\n method of idatastream will return).\n' - -Repository: Products.PortalTransforms - - -Branch: refs/heads/master -Date: 2024-11-28T11:29:26-03:00 -Author: Peter Mathis (petschki) -Commit: https://github.com/plone/Products.PortalTransforms/commit/425f7ea4cc30a880f65a2591722295579479c173 - -changenote - -Files changed: -A news/70.bugfix - -b'diff --git a/news/70.bugfix b/news/70.bugfix\nnew file mode 100644\nindex 0000000..da7158a\n--- /dev/null\n+++ b/news/70.bugfix\n@@ -0,0 +1,2 @@\n+Fix removed `unittest.makeSuite` in python 3.13\n+[petschki]\n' - -Repository: Products.PortalTransforms - - -Branch: refs/heads/master -Date: 2024-11-28T11:31:07-03:00 -Author: Peter Mathis (petschki) -Commit: https://github.com/plone/Products.PortalTransforms/commit/a6c5cd868673af5ce07c9912e25579c4adb725d6 - -black +See https://github.com/plone/Products.CMFPlone/pull/4054 Files changed: -M Products/PortalTransforms/tests/test_intelligenttext.py +M src/plone/app/theming/browser/controlpanel.pt -b'diff --git a/Products/PortalTransforms/tests/test_intelligenttext.py b/Products/PortalTransforms/tests/test_intelligenttext.py\nindex f25f9f5..63dc934 100644\n--- a/Products/PortalTransforms/tests/test_intelligenttext.py\n+++ b/Products/PortalTransforms/tests/test_intelligenttext.py\n@@ -157,7 +157,9 @@ def testWhitespace(self):\n def test_suite():\n import unittest\n \n- return unittest.TestSuite((\n- unittest.defaultTestLoader.loadTestsFromTestCase(TestIntelligentTextToHtml),\n- unittest.defaultTestLoader.loadTestsFromTestCase(TestHtmlToIntelligentText),\n- ))\n+ return unittest.TestSuite(\n+ (\n+ unittest.defaultTestLoader.loadTestsFromTestCase(TestIntelligentTextToHtml),\n+ unittest.defaultTestLoader.loadTestsFromTestCase(TestHtmlToIntelligentText),\n+ )\n+ )\n' +b'diff --git a/src/plone/app/theming/browser/controlpanel.pt b/src/plone/app/theming/browser/controlpanel.pt\nindex 382f638..621c8d5 100644\n--- a/src/plone/app/theming/browser/controlpanel.pt\n+++ b/src/plone/app/theming/browser/controlpanel.pt\n@@ -725,10 +725,10 @@\n Define your own custom CSS in the field below. This is a good place for quick customizations of things like colors and the toolbar. Definitions here will override previously defined CSS of Plone. Please use this only for small customizations, as it is hard to keep track of changes here. For bigger changes you most likely want to customize a full theme and make your changes there.\n \n \n \n
-Commit: https://github.com/plone/Products.PortalTransforms/commit/0d822211a65bf3abdb367d88ad70e1bfea616f88 +Date: 2024-12-03T20:09:51-08:00 +Author: Steve Piercy (stevepiercy) +Commit: https://github.com/plone/plone.app.theming/commit/dc15462a8c93c7c0467aee5b4767a412884a1b2e -revert changes in demo output files +Create 248.bugfix Files changed: -M Products/PortalTransforms/tests/output/demo1.html -M Products/PortalTransforms/tests/output/demo1.html.nofilename +A news/248.bugfix -b'diff --git a/Products/PortalTransforms/tests/output/demo1.html b/Products/PortalTransforms/tests/output/demo1.html\nindex 5457311..2f60f41 100644\n--- a/Products/PortalTransforms/tests/output/demo1.html\n+++ b/Products/PortalTransforms/tests/output/demo1.html\n@@ -1 +1 @@\n-Chapter 44
Writing Basic Unit Tests
Di\xef\xac\x83culty
Newcomer
Skills
\xe2\x80\xa2 All you need to know is some Python.
Problem/Task
As you know by now, Zope 3 gains its incredible stability from testing any code in great detail. The
currently most common method is to write unit tests. This chapter introduces unit tests \xe2\x80\x93 which
are Zope 3 independent \xe2\x80\x93 and introduces some of the subtleties.
Solution
44.1
Implementing the Sample Class
Before we can write tests, we have to write some code that we can test. Here, we will implement
a simple class called Sample with a public attribute title and description that is accessed
via getDescription() and mutated using setDescription(). Further, the description must be
either a regular or unicode string.
Since this code will not depend on Zope, open a \xef\xac\x81le named test sample.py anywhere and add
the following class:
1 Sample(object):
2
"""A trivial Sample object."""
3
4
title = None
5
6
def __init__(self):
7
"""Initialize object."""
8
self._description = \xe2\x80\x99\xe2\x80\x99
9
1

2
CHAPTER 44. WRITING BASIC UNIT TESTS
10
def setDescription(self, value):
11
"""Change the value of the description."""
12
assert isinstance(value, (str, unicode))
13
self._description = value
14
15
def getDescription(self):
16
"""Change the value of the description."""
17
return self._description
Line 4: The title is just publicly declared and a value of None is given. Therefore this is just
a regular attribute.
Line 8: The actual description string will be stored in description.
Line 12: Make sure that the description is only a regular or unicode string, like it was stated in
the requirements.
If you wish you can now manually test the class with the interactive Python shell. Just start
Python by entering python in your shell prompt. Note that you should be in the directory in
which test sample.py is located when starting Python (an alternative is of course to specify the
directory in your PYTHONPATH.)
1 >>> from test_sample import Sample
2 >>> sample = Sample()
3 >>> print sample.title
4 None
5 >>> sample.title = \xe2\x80\x99Title\xe2\x80\x99
6 >>> print sample.title
7 Title
8 >>> print sample.getDescription()
9
10 >>> sample.setDescription(\xe2\x80\x99Hello World\xe2\x80\x99)
11 >>> print sample.getDescription()
12 Hello World
13 >>> sample.setDescription(None)
14 Traceback (most recent call last):
15
File "<stdin>", line 1, in ?
16
File "test_sample.py", line 31, in setDescription
17
assert isinstance(value, (str, unicode))
18 AssertionError
As you can see in the last test, non-string object types are not allowed as descriptions and an
AssertionError is raised.
44.2
Writing the Unit Tests
The goal of writing the unit tests is to convert this informal, manual, and interactive testing session
into a formal test class. Python provides already a module called unittest for this purpose, which
is a port of the Java-based unit testing product, JUnit, by Kent Beck and Erich Gamma. There are
three levels to the testing framework (this list deviates a bit from the original de\xef\xac\x81nitions as found
in the Python library documentation. 1).
1 http://www.python.org/doc/current/lib/module-unittest.html

44.2. WRITING THE UNIT TESTS
3
The smallest unit is obviously the \xe2\x80\x9ctest\xe2\x80\x9d, which is a single method in a TestCase class that
tests the behavior of a small piece of code or a particular aspect of an implementation. The \xe2\x80\x9ctest
case\xe2\x80\x9d is then a collection tests that share the same setup/inputs. On top of all of this sits the \xe2\x80\x9ctest
suite\xe2\x80\x9d which is a collection of test cases and/or other test suites. Test suites combine tests that
should be executed together. With the correct setup (as shown in the example below), you can
then execute test suites. For large projects like Zope 3, it is useful to know that there is also the
concept of a test runner, which manages the test run of all or a set of tests. The runner provides
useful feedback to the application, so that various user interfaces can be developed on top of it.
But enough about the theory. In the following example, which you can simply put into the same
\xef\xac\x81le as your code above, you will see a test in common Zope 3 style.
1 import unittest
2
3 class SampleTest(unittest.TestCase):
4
"""Test the Sample class"""
5
6
def test_title(self):
7
sample = Sample()
8
self.assertEqual(sample.title, None)
9
sample.title = \xe2\x80\x99Sample Title\xe2\x80\x99
10
self.assertEqual(sample.title, \xe2\x80\x99Sample Title\xe2\x80\x99)
11
12
def test_getDescription(self):
13
sample = Sample()
14
self.assertEqual(sample.getDescription(), \xe2\x80\x99\xe2\x80\x99)
15
sample._description = "Description"
16
self.assertEqual(sample.getDescription(), \xe2\x80\x99Description\xe2\x80\x99)
17
18
def test_setDescription(self):
19
sample = Sample()
20
self.assertEqual(sample._description, \xe2\x80\x99\xe2\x80\x99)
21
sample.setDescription(\xe2\x80\x99Description\xe2\x80\x99)
22
self.assertEqual(sample._description, \xe2\x80\x99Description\xe2\x80\x99)
23
sample.setDescription(u\xe2\x80\x99Description2\xe2\x80\x99)
24
self.assertEqual(sample._description, u\xe2\x80\x99Description2\xe2\x80\x99)
25
self.assertRaises(AssertionError, sample.setDescription, None)
26
27
28 def test_suite():
29
return unittest.TestSuite((
30
unittest.defaultTestLoader.loadTestsFromTestCase(SampleTest),
31
))
32
33 if __name__ == \xe2\x80\x99__main__\xe2\x80\x99:
34
unittest.main(defaultTest=\xe2\x80\x99test_suite\xe2\x80\x99)
Line 3\xe2\x80\x934: We usually develop test classes which must inherit from TestCase. While often not
done, it is a good idea to give the class a meaningful docstring that describes the purpose of the
tests it includes.
Line 6, 12 & 18: When a test case is run, a method called runTests() is executed. While it
is possible to override this method to run tests di\xef\xac\x80erently, the default option will look for any
method whose name starts with test and execute it as a single test. This way we can create
a \xe2\x80\x9ctest method\xe2\x80\x9d for each aspect, method, function or property of the code to be tested. This
default is very sensible and is used everywhere in Zope 3.

4
CHAPTER 44. WRITING BASIC UNIT TESTS
Note that there is no docstring for test methods. This is intentional. If a docstring is speci\xef\xac\x81ed,
it is used instead of the method name to identify the test. When specifying a docstring, we have
noticed that it is very di\xef\xac\x83cult to identify the test later; therefore the method name is a much
better choice.
Line 8, 10, 14, . . . : The TestCase class implements a handful of methods that aid you with the
testing. Here are some of the most frequently used ones. For a complete list see the standard
Python documentation referenced above.
\xe2\x80\xa2 assertEqual(first,second[,msg])
Checks whether the first and second value are equal. If the test fails, the msg or None
is returned.
\xe2\x80\xa2 assertNotEqual(first,second[,msg])
This is simply the opposite to assertEqual() by checking for non-equality.
\xe2\x80\xa2 assertRaises(exception,callable,...)
You expect the callable to raise exception when executed. After the callable you can
specify any amount of positional and keyword arguments for the callable. If you expect
a group of exceptions from the execution, you can make exception a tuple of possible
exceptions.
\xe2\x80\xa2 assert (expr[,msg])
Assert checks whether the speci\xef\xac\x81ed expression executes correctly. If not, the test fails and
msg or None is returned.
\xe2\x80\xa2 assertEqual()
This testing method is equivalent to assertEqual().
\xe2\x80\xa2 assertTrue(expr[,msg])
This method is equivalent to assert (expr[,msg]).
\xe2\x80\xa2 assertFalse()
This is the opposite to assertTrue().
\xe2\x80\xa2 fail([msg])
Fails the running test without any evaluation. This is commonly used when testing various
possible execution paths at once and you would like to signify a failure if an improper path
was taken.
Line 6\xe2\x80\x9310: This method tests the title attribute of the Sample class. The \xef\xac\x81rst test should
be of course that the attribute exists and has the expected initial value (line 8). Then the title
attribute is changed and we check whether the value was really stored. This might seem like
overkill, but later you might change the title in a way that it uses properties instead. Then it
becomes very important to check whether this test still passes.
Line 12\xe2\x80\x9316: First we simply check that getDescription() returns the correct default value.
Since we do not want to use other API calls like setDescription() we set a new value of the
description via the implementation-internal description attribute (line 15). This is okay! Unit
tests can make use of implementation-speci\xef\xac\x81c attributes and methods. Finally we just check that
the correct value is returned.

44.3. RUNNING THE TESTS
5
Line 18\xe2\x80\x9325: On line 21\xe2\x80\x9324 it is checked that both regular and unicode strings are set correctly.
In the last line of the test we make sure that no other type of objects can be set as a description
and that an error is raised.
28\xe2\x80\x9331: This method returns a test suite that includes all test cases created in this module. It is
used by the Zope 3 test runner when it picks up all available tests. You would basically add the
line unittest.defaultTestLoader.loadTestsFromTestCase(TestCaseClass) for each additional test case.
33\xe2\x80\x9334: In order to make the test module runnable by itself, you can execute unittest.main()
when the module is run.
44.3
Running the Tests
You can run the test by simply calling pythontest sample.py from the directory you saved the
\xef\xac\x81le in. Here is the result you should see:
.
--------------------------------------------------------------------
n 3 tests in 0.001s
The three dots represent the three tests that were run. If a test had failed, it would have been
reported pointing out the failing test and providing a small traceback.
When using the default Zope 3 test runner, tests will be picked up as long as they follow some
conventions.
\xe2\x80\xa2 The tests must either be in a package or be a module called tests.
\xe2\x80\xa2 If tests is a package, then all test modules inside must also have a name starting with test,
as it is the case with our name test sample.py.
\xe2\x80\xa2 The test module must be somewhere in the Zope 3 source tree, since the test runner looks
only for \xef\xac\x81les there.
In our case, you could simply create a tests package in ZOPE3/src (do not forget the
init .
py \xef\xac\x81le). Then place the test sample.py \xef\xac\x81le into this directory.
You you can use the test runner to run only the sample tests as follows from the Zope 3 root
directory:
python test.py -vp tests.test_sample
The -v option stands for verbose mode, so that detailed information about a test failure is
provided. The -p option enables a progress bar that tells you how many tests out of all have been
completed. There are many more options that can be speci\xef\xac\x81ed. You can get a full list of them with
the option -h: pythontest.py-h.
The output of the call above is as follows:
nfiguration file found.
nning UNIT tests at level 1
nning UNIT tests from /opt/zope/Zope3
3/3 (100.0%): test_title (tests.test_sample.SampleTest)
--------------------------------------------------------------------
n 3 tests in 0.002s

6
CHAPTER 44. WRITING BASIC UNIT TESTS
nning FUNCTIONAL tests at level 1
nning FUNCTIONAL tests from /opt/zope/Zope3
--------------------------------------------------------------------
n 0 tests in 0.000s
Line 1: The test runner uses a con\xef\xac\x81guration \xef\xac\x81le for some setup. This allows developers to use
the test runner for other projects as well. This message simply tells us that the con\xef\xac\x81guration \xef\xac\x81le
was found.
Line 2\xe2\x80\x938: The unit tests are run. On line 4 you can see the progress bar.
Line 9\xe2\x80\x9315: The functional tests are run, since the default test runner runs both types of tests.
Since we do not have any functional tests in the speci\xef\xac\x81ed module, there are no tests to run. To
just run the unit tests, use option -u and -f for just running the functional tests. See \xe2\x80\x9cWriting
Functional Tests\xe2\x80\x9d for more details on functional tests.

44.3. RUNNING THE TESTS
7
Exercises
1. It is not very common to do the setup \xe2\x80\x93 in our case sample=Sample() \xe2\x80\x93 in every test
method. Instead there exists a method called setUp() and its counterpart tearDown that
are run before and after each test, respectively. Change the test code above, so that it uses
the setUp() method. In later chapters and the rest of the book we will frequently use this
method of setting up tests.
2. Currently the test setDescription() test only veri\xef\xac\x81es that None is not allowed as input
value.
(a) Improve the test, so that all other builtin types are tested as well.
(b) Also, make sure that any objects inheriting from str or unicode pass as valid values.

\n+Chapter 44
Writing Basic Unit Tests
Di\xef\xac\x83culty
Newcomer
Skills
\xe2\x80\xa2 All you need to know is some Python.
Problem/Task
As you know by now, Zope 3 gains its incredible stability from testing any code in great detail. The
currently most common method is to write unit tests. This chapter introduces unit tests \xe2\x80\x93 which
are Zope 3 independent \xe2\x80\x93 and introduces some of the subtleties.
Solution
44.1
Implementing the Sample Class
Before we can write tests, we have to write some code that we can test. Here, we will implement
a simple class called Sample with a public attribute title and description that is accessed
via getDescription() and mutated using setDescription(). Further, the description must be
either a regular or unicode string.
Since this code will not depend on Zope, open a \xef\xac\x81le named test sample.py anywhere and add
the following class:
1 Sample(object):
2
"""A trivial Sample object."""
3
4
title = None
5
6
def __init__(self):
7
"""Initialize object."""
8
self._description = \xe2\x80\x99\xe2\x80\x99
9
1

2
CHAPTER 44. WRITING BASIC UNIT TESTS
10
def setDescription(self, value):
11
"""Change the value of the description."""
12
assert isinstance(value, (str, unicode))
13
self._description = value
14
15
def getDescription(self):
16
"""Change the value of the description."""
17
return self._description
Line 4: The title is just publicly declared and a value of None is given. Therefore this is just
a regular attribute.
Line 8: The actual description string will be stored in description.
Line 12: Make sure that the description is only a regular or unicode string, like it was stated in
the requirements.
If you wish you can now manually test the class with the interactive Python shell. Just start
Python by entering python in your shell prompt. Note that you should be in the directory in
which test sample.py is located when starting Python (an alternative is of course to specify the
directory in your PYTHONPATH.)
1 >>> from test_sample import Sample
2 >>> sample = Sample()
3 >>> print sample.title
4 None
5 >>> sample.title = \xe2\x80\x99Title\xe2\x80\x99
6 >>> print sample.title
7 Title
8 >>> print sample.getDescription()
9
10 >>> sample.setDescription(\xe2\x80\x99Hello World\xe2\x80\x99)
11 >>> print sample.getDescription()
12 Hello World
13 >>> sample.setDescription(None)
14 Traceback (most recent call last):
15
File "<stdin>", line 1, in ?
16
File "test_sample.py", line 31, in setDescription
17
assert isinstance(value, (str, unicode))
18 AssertionError
As you can see in the last test, non-string object types are not allowed as descriptions and an
AssertionError is raised.
44.2
Writing the Unit Tests
The goal of writing the unit tests is to convert this informal, manual, and interactive testing session
into a formal test class. Python provides already a module called unittest for this purpose, which
is a port of the Java-based unit testing product, JUnit, by Kent Beck and Erich Gamma. There are
three levels to the testing framework (this list deviates a bit from the original de\xef\xac\x81nitions as found
in the Python library documentation. 1).
1 http://www.python.org/doc/current/lib/module-unittest.html

44.2. WRITING THE UNIT TESTS
3
The smallest unit is obviously the \xe2\x80\x9ctest\xe2\x80\x9d, which is a single method in a TestCase class that
tests the behavior of a small piece of code or a particular aspect of an implementation. The \xe2\x80\x9ctest
case\xe2\x80\x9d is then a collection tests that share the same setup/inputs. On top of all of this sits the \xe2\x80\x9ctest
suite\xe2\x80\x9d which is a collection of test cases and/or other test suites. Test suites combine tests that
should be executed together. With the correct setup (as shown in the example below), you can
then execute test suites. For large projects like Zope 3, it is useful to know that there is also the
concept of a test runner, which manages the test run of all or a set of tests. The runner provides
useful feedback to the application, so that various user interfaces can be developed on top of it.
But enough about the theory. In the following example, which you can simply put into the same
\xef\xac\x81le as your code above, you will see a test in common Zope 3 style.
1 import unittest
2
3 class SampleTest(unittest.TestCase):
4
"""Test the Sample class"""
5
6
def test_title(self):
7
sample = Sample()
8
self.assertEqual(sample.title, None)
9
sample.title = \xe2\x80\x99Sample Title\xe2\x80\x99
10
self.assertEqual(sample.title, \xe2\x80\x99Sample Title\xe2\x80\x99)
11
12
def test_getDescription(self):
13
sample = Sample()
14
self.assertEqual(sample.getDescription(), \xe2\x80\x99\xe2\x80\x99)
15
sample._description = "Description"
16
self.assertEqual(sample.getDescription(), \xe2\x80\x99Description\xe2\x80\x99)
17
18
def test_setDescription(self):
19
sample = Sample()
20
self.assertEqual(sample._description, \xe2\x80\x99\xe2\x80\x99)
21
sample.setDescription(\xe2\x80\x99Description\xe2\x80\x99)
22
self.assertEqual(sample._description, \xe2\x80\x99Description\xe2\x80\x99)
23
sample.setDescription(u\xe2\x80\x99Description2\xe2\x80\x99)
24
self.assertEqual(sample._description, u\xe2\x80\x99Description2\xe2\x80\x99)
25
self.assertRaises(AssertionError, sample.setDescription, None)
26
27
28 def test_suite():
29
return unittest.TestSuite((
30
unittest.makeSuite(SampleTest),
31
))
32
33 if __name__ == \xe2\x80\x99__main__\xe2\x80\x99:
34
unittest.main(defaultTest=\xe2\x80\x99test_suite\xe2\x80\x99)
Line 3\xe2\x80\x934: We usually develop test classes which must inherit from TestCase. While often not
done, it is a good idea to give the class a meaningful docstring that describes the purpose of the
tests it includes.
Line 6, 12 & 18: When a test case is run, a method called runTests() is executed. While it
is possible to override this method to run tests di\xef\xac\x80erently, the default option will look for any
method whose name starts with test and execute it as a single test. This way we can create
a \xe2\x80\x9ctest method\xe2\x80\x9d for each aspect, method, function or property of the code to be tested. This
default is very sensible and is used everywhere in Zope 3.

4
CHAPTER 44. WRITING BASIC UNIT TESTS
Note that there is no docstring for test methods. This is intentional. If a docstring is speci\xef\xac\x81ed,
it is used instead of the method name to identify the test. When specifying a docstring, we have
noticed that it is very di\xef\xac\x83cult to identify the test later; therefore the method name is a much
better choice.
Line 8, 10, 14, . . . : The TestCase class implements a handful of methods that aid you with the
testing. Here are some of the most frequently used ones. For a complete list see the standard
Python documentation referenced above.
\xe2\x80\xa2 assertEqual(first,second[,msg])
Checks whether the first and second value are equal. If the test fails, the msg or None
is returned.
\xe2\x80\xa2 assertNotEqual(first,second[,msg])
This is simply the opposite to assertEqual() by checking for non-equality.
\xe2\x80\xa2 assertRaises(exception,callable,...)
You expect the callable to raise exception when executed. After the callable you can
specify any amount of positional and keyword arguments for the callable. If you expect
a group of exceptions from the execution, you can make exception a tuple of possible
exceptions.
\xe2\x80\xa2 assert (expr[,msg])
Assert checks whether the speci\xef\xac\x81ed expression executes correctly. If not, the test fails and
msg or None is returned.
\xe2\x80\xa2 assertEqual()
This testing method is equivalent to assertEqual().
\xe2\x80\xa2 assertTrue(expr[,msg])
This method is equivalent to assert (expr[,msg]).
\xe2\x80\xa2 assertFalse()
This is the opposite to assertTrue().
\xe2\x80\xa2 fail([msg])
Fails the running test without any evaluation. This is commonly used when testing various
possible execution paths at once and you would like to signify a failure if an improper path
was taken.
Line 6\xe2\x80\x9310: This method tests the title attribute of the Sample class. The \xef\xac\x81rst test should
be of course that the attribute exists and has the expected initial value (line 8). Then the title
attribute is changed and we check whether the value was really stored. This might seem like
overkill, but later you might change the title in a way that it uses properties instead. Then it
becomes very important to check whether this test still passes.
Line 12\xe2\x80\x9316: First we simply check that getDescription() returns the correct default value.
Since we do not want to use other API calls like setDescription() we set a new value of the
description via the implementation-internal description attribute (line 15). This is okay! Unit
tests can make use of implementation-speci\xef\xac\x81c attributes and methods. Finally we just check that
the correct value is returned.

44.3. RUNNING THE TESTS
5
Line 18\xe2\x80\x9325: On line 21\xe2\x80\x9324 it is checked that both regular and unicode strings are set correctly.
In the last line of the test we make sure that no other type of objects can be set as a description
and that an error is raised.
28\xe2\x80\x9331: This method returns a test suite that includes all test cases created in this module. It is
used by the Zope 3 test runner when it picks up all available tests. You would basically add the
line unittest.makeSuite(TestCaseClass) for each additional test case.
33\xe2\x80\x9334: In order to make the test module runnable by itself, you can execute unittest.main()
when the module is run.
44.3
Running the Tests
You can run the test by simply calling pythontest sample.py from the directory you saved the
\xef\xac\x81le in. Here is the result you should see:
.
--------------------------------------------------------------------
n 3 tests in 0.001s
The three dots represent the three tests that were run. If a test had failed, it would have been
reported pointing out the failing test and providing a small traceback.
When using the default Zope 3 test runner, tests will be picked up as long as they follow some
conventions.
\xe2\x80\xa2 The tests must either be in a package or be a module called tests.
\xe2\x80\xa2 If tests is a package, then all test modules inside must also have a name starting with test,
as it is the case with our name test sample.py.
\xe2\x80\xa2 The test module must be somewhere in the Zope 3 source tree, since the test runner looks
only for \xef\xac\x81les there.
In our case, you could simply create a tests package in ZOPE3/src (do not forget the
init .
py \xef\xac\x81le). Then place the test sample.py \xef\xac\x81le into this directory.
You you can use the test runner to run only the sample tests as follows from the Zope 3 root
directory:
python test.py -vp tests.test_sample
The -v option stands for verbose mode, so that detailed information about a test failure is
provided. The -p option enables a progress bar that tells you how many tests out of all have been
completed. There are many more options that can be speci\xef\xac\x81ed. You can get a full list of them with
the option -h: pythontest.py-h.
The output of the call above is as follows:
nfiguration file found.
nning UNIT tests at level 1
nning UNIT tests from /opt/zope/Zope3
3/3 (100.0%): test_title (tests.test_sample.SampleTest)
--------------------------------------------------------------------
n 3 tests in 0.002s

6
CHAPTER 44. WRITING BASIC UNIT TESTS
nning FUNCTIONAL tests at level 1
nning FUNCTIONAL tests from /opt/zope/Zope3
--------------------------------------------------------------------
n 0 tests in 0.000s
Line 1: The test runner uses a con\xef\xac\x81guration \xef\xac\x81le for some setup. This allows developers to use
the test runner for other projects as well. This message simply tells us that the con\xef\xac\x81guration \xef\xac\x81le
was found.
Line 2\xe2\x80\x938: The unit tests are run. On line 4 you can see the progress bar.
Line 9\xe2\x80\x9315: The functional tests are run, since the default test runner runs both types of tests.
Since we do not have any functional tests in the speci\xef\xac\x81ed module, there are no tests to run. To
just run the unit tests, use option -u and -f for just running the functional tests. See \xe2\x80\x9cWriting
Functional Tests\xe2\x80\x9d for more details on functional tests.

44.3. RUNNING THE TESTS
7
Exercises
1. It is not very common to do the setup \xe2\x80\x93 in our case sample=Sample() \xe2\x80\x93 in every test
method. Instead there exists a method called setUp() and its counterpart tearDown that
are run before and after each test, respectively. Change the test code above, so that it uses
the setUp() method. In later chapters and the rest of the book we will frequently use this
method of setting up tests.
2. Currently the test setDescription() test only veri\xef\xac\x81es that None is not allowed as input
value.
(a) Improve the test, so that all other builtin types are tested as well.
(b) Also, make sure that any objects inheriting from str or unicode pass as valid values.

\ndiff --git a/Products/PortalTransforms/tests/output/demo1.html.nofilename b/Products/PortalTransforms/tests/output/demo1.html.nofilename\nindex 5457311..2f60f41 100644\n--- a/Products/PortalTransforms/tests/output/demo1.html.nofilename\n+++ b/Products/PortalTransforms/tests/output/demo1.html.nofilename\n@@ -1 +1 @@\n-Chapter 44
Writing Basic Unit Tests
Di\xef\xac\x83culty
Newcomer
Skills
\xe2\x80\xa2 All you need to know is some Python.
Problem/Task
As you know by now, Zope 3 gains its incredible stability from testing any code in great detail. The
currently most common method is to write unit tests. This chapter introduces unit tests \xe2\x80\x93 which
are Zope 3 independent \xe2\x80\x93 and introduces some of the subtleties.
Solution
44.1
Implementing the Sample Class
Before we can write tests, we have to write some code that we can test. Here, we will implement
a simple class called Sample with a public attribute title and description that is accessed
via getDescription() and mutated using setDescription(). Further, the description must be
either a regular or unicode string.
Since this code will not depend on Zope, open a \xef\xac\x81le named test sample.py anywhere and add
the following class:
1 Sample(object):
2
"""A trivial Sample object."""
3
4
title = None
5
6
def __init__(self):
7
"""Initialize object."""
8
self._description = \xe2\x80\x99\xe2\x80\x99
9
1

2
CHAPTER 44. WRITING BASIC UNIT TESTS
10
def setDescription(self, value):
11
"""Change the value of the description."""
12
assert isinstance(value, (str, unicode))
13
self._description = value
14
15
def getDescription(self):
16
"""Change the value of the description."""
17
return self._description
Line 4: The title is just publicly declared and a value of None is given. Therefore this is just
a regular attribute.
Line 8: The actual description string will be stored in description.
Line 12: Make sure that the description is only a regular or unicode string, like it was stated in
the requirements.
If you wish you can now manually test the class with the interactive Python shell. Just start
Python by entering python in your shell prompt. Note that you should be in the directory in
which test sample.py is located when starting Python (an alternative is of course to specify the
directory in your PYTHONPATH.)
1 >>> from test_sample import Sample
2 >>> sample = Sample()
3 >>> print sample.title
4 None
5 >>> sample.title = \xe2\x80\x99Title\xe2\x80\x99
6 >>> print sample.title
7 Title
8 >>> print sample.getDescription()
9
10 >>> sample.setDescription(\xe2\x80\x99Hello World\xe2\x80\x99)
11 >>> print sample.getDescription()
12 Hello World
13 >>> sample.setDescription(None)
14 Traceback (most recent call last):
15
File "<stdin>", line 1, in ?
16
File "test_sample.py", line 31, in setDescription
17
assert isinstance(value, (str, unicode))
18 AssertionError
As you can see in the last test, non-string object types are not allowed as descriptions and an
AssertionError is raised.
44.2
Writing the Unit Tests
The goal of writing the unit tests is to convert this informal, manual, and interactive testing session
into a formal test class. Python provides already a module called unittest for this purpose, which
is a port of the Java-based unit testing product, JUnit, by Kent Beck and Erich Gamma. There are
three levels to the testing framework (this list deviates a bit from the original de\xef\xac\x81nitions as found
in the Python library documentation. 1).
1 http://www.python.org/doc/current/lib/module-unittest.html

44.2. WRITING THE UNIT TESTS
3
The smallest unit is obviously the \xe2\x80\x9ctest\xe2\x80\x9d, which is a single method in a TestCase class that
tests the behavior of a small piece of code or a particular aspect of an implementation. The \xe2\x80\x9ctest
case\xe2\x80\x9d is then a collection tests that share the same setup/inputs. On top of all of this sits the \xe2\x80\x9ctest
suite\xe2\x80\x9d which is a collection of test cases and/or other test suites. Test suites combine tests that
should be executed together. With the correct setup (as shown in the example below), you can
then execute test suites. For large projects like Zope 3, it is useful to know that there is also the
concept of a test runner, which manages the test run of all or a set of tests. The runner provides
useful feedback to the application, so that various user interfaces can be developed on top of it.
But enough about the theory. In the following example, which you can simply put into the same
\xef\xac\x81le as your code above, you will see a test in common Zope 3 style.
1 import unittest
2
3 class SampleTest(unittest.TestCase):
4
"""Test the Sample class"""
5
6
def test_title(self):
7
sample = Sample()
8
self.assertEqual(sample.title, None)
9
sample.title = \xe2\x80\x99Sample Title\xe2\x80\x99
10
self.assertEqual(sample.title, \xe2\x80\x99Sample Title\xe2\x80\x99)
11
12
def test_getDescription(self):
13
sample = Sample()
14
self.assertEqual(sample.getDescription(), \xe2\x80\x99\xe2\x80\x99)
15
sample._description = "Description"
16
self.assertEqual(sample.getDescription(), \xe2\x80\x99Description\xe2\x80\x99)
17
18
def test_setDescription(self):
19
sample = Sample()
20
self.assertEqual(sample._description, \xe2\x80\x99\xe2\x80\x99)
21
sample.setDescription(\xe2\x80\x99Description\xe2\x80\x99)
22
self.assertEqual(sample._description, \xe2\x80\x99Description\xe2\x80\x99)
23
sample.setDescription(u\xe2\x80\x99Description2\xe2\x80\x99)
24
self.assertEqual(sample._description, u\xe2\x80\x99Description2\xe2\x80\x99)
25
self.assertRaises(AssertionError, sample.setDescription, None)
26
27
28 def test_suite():
29
return unittest.TestSuite((
30
unittest.defaultTestLoader.loadTestsFromTestCase(SampleTest),
31
))
32
33 if __name__ == \xe2\x80\x99__main__\xe2\x80\x99:
34
unittest.main(defaultTest=\xe2\x80\x99test_suite\xe2\x80\x99)
Line 3\xe2\x80\x934: We usually develop test classes which must inherit from TestCase. While often not
done, it is a good idea to give the class a meaningful docstring that describes the purpose of the
tests it includes.
Line 6, 12 & 18: When a test case is run, a method called runTests() is executed. While it
is possible to override this method to run tests di\xef\xac\x80erently, the default option will look for any
method whose name starts with test and execute it as a single test. This way we can create
a \xe2\x80\x9ctest method\xe2\x80\x9d for each aspect, method, function or property of the code to be tested. This
default is very sensible and is used everywhere in Zope 3.

4
CHAPTER 44. WRITING BASIC UNIT TESTS
Note that there is no docstring for test methods. This is intentional. If a docstring is speci\xef\xac\x81ed,
it is used instead of the method name to identify the test. When specifying a docstring, we have
noticed that it is very di\xef\xac\x83cult to identify the test later; therefore the method name is a much
better choice.
Line 8, 10, 14, . . . : The TestCase class implements a handful of methods that aid you with the
testing. Here are some of the most frequently used ones. For a complete list see the standard
Python documentation referenced above.
\xe2\x80\xa2 assertEqual(first,second[,msg])
Checks whether the first and second value are equal. If the test fails, the msg or None
is returned.
\xe2\x80\xa2 assertNotEqual(first,second[,msg])
This is simply the opposite to assertEqual() by checking for non-equality.
\xe2\x80\xa2 assertRaises(exception,callable,...)
You expect the callable to raise exception when executed. After the callable you can
specify any amount of positional and keyword arguments for the callable. If you expect
a group of exceptions from the execution, you can make exception a tuple of possible
exceptions.
\xe2\x80\xa2 assert (expr[,msg])
Assert checks whether the speci\xef\xac\x81ed expression executes correctly. If not, the test fails and
msg or None is returned.
\xe2\x80\xa2 assertEqual()
This testing method is equivalent to assertEqual().
\xe2\x80\xa2 assertTrue(expr[,msg])
This method is equivalent to assert (expr[,msg]).
\xe2\x80\xa2 assertFalse()
This is the opposite to assertTrue().
\xe2\x80\xa2 fail([msg])
Fails the running test without any evaluation. This is commonly used when testing various
possible execution paths at once and you would like to signify a failure if an improper path
was taken.
Line 6\xe2\x80\x9310: This method tests the title attribute of the Sample class. The \xef\xac\x81rst test should
be of course that the attribute exists and has the expected initial value (line 8). Then the title
attribute is changed and we check whether the value was really stored. This might seem like
overkill, but later you might change the title in a way that it uses properties instead. Then it
becomes very important to check whether this test still passes.
Line 12\xe2\x80\x9316: First we simply check that getDescription() returns the correct default value.
Since we do not want to use other API calls like setDescription() we set a new value of the
description via the implementation-internal description attribute (line 15). This is okay! Unit
tests can make use of implementation-speci\xef\xac\x81c attributes and methods. Finally we just check that
the correct value is returned.

44.3. RUNNING THE TESTS
5
Line 18\xe2\x80\x9325: On line 21\xe2\x80\x9324 it is checked that both regular and unicode strings are set correctly.
In the last line of the test we make sure that no other type of objects can be set as a description
and that an error is raised.
28\xe2\x80\x9331: This method returns a test suite that includes all test cases created in this module. It is
used by the Zope 3 test runner when it picks up all available tests. You would basically add the
line unittest.defaultTestLoader.loadTestsFromTestCase(TestCaseClass) for each additional test case.
33\xe2\x80\x9334: In order to make the test module runnable by itself, you can execute unittest.main()
when the module is run.
44.3
Running the Tests
You can run the test by simply calling pythontest sample.py from the directory you saved the
\xef\xac\x81le in. Here is the result you should see:
.
--------------------------------------------------------------------
n 3 tests in 0.001s
The three dots represent the three tests that were run. If a test had failed, it would have been
reported pointing out the failing test and providing a small traceback.
When using the default Zope 3 test runner, tests will be picked up as long as they follow some
conventions.
\xe2\x80\xa2 The tests must either be in a package or be a module called tests.
\xe2\x80\xa2 If tests is a package, then all test modules inside must also have a name starting with test,
as it is the case with our name test sample.py.
\xe2\x80\xa2 The test module must be somewhere in the Zope 3 source tree, since the test runner looks
only for \xef\xac\x81les there.
In our case, you could simply create a tests package in ZOPE3/src (do not forget the
init .
py \xef\xac\x81le). Then place the test sample.py \xef\xac\x81le into this directory.
You you can use the test runner to run only the sample tests as follows from the Zope 3 root
directory:
python test.py -vp tests.test_sample
The -v option stands for verbose mode, so that detailed information about a test failure is
provided. The -p option enables a progress bar that tells you how many tests out of all have been
completed. There are many more options that can be speci\xef\xac\x81ed. You can get a full list of them with
the option -h: pythontest.py-h.
The output of the call above is as follows:
nfiguration file found.
nning UNIT tests at level 1
nning UNIT tests from /opt/zope/Zope3
3/3 (100.0%): test_title (tests.test_sample.SampleTest)
--------------------------------------------------------------------
n 3 tests in 0.002s

6
CHAPTER 44. WRITING BASIC UNIT TESTS
nning FUNCTIONAL tests at level 1
nning FUNCTIONAL tests from /opt/zope/Zope3
--------------------------------------------------------------------
n 0 tests in 0.000s
Line 1: The test runner uses a con\xef\xac\x81guration \xef\xac\x81le for some setup. This allows developers to use
the test runner for other projects as well. This message simply tells us that the con\xef\xac\x81guration \xef\xac\x81le
was found.
Line 2\xe2\x80\x938: The unit tests are run. On line 4 you can see the progress bar.
Line 9\xe2\x80\x9315: The functional tests are run, since the default test runner runs both types of tests.
Since we do not have any functional tests in the speci\xef\xac\x81ed module, there are no tests to run. To
just run the unit tests, use option -u and -f for just running the functional tests. See \xe2\x80\x9cWriting
Functional Tests\xe2\x80\x9d for more details on functional tests.

44.3. RUNNING THE TESTS
7
Exercises
1. It is not very common to do the setup \xe2\x80\x93 in our case sample=Sample() \xe2\x80\x93 in every test
method. Instead there exists a method called setUp() and its counterpart tearDown that
are run before and after each test, respectively. Change the test code above, so that it uses
the setUp() method. In later chapters and the rest of the book we will frequently use this
method of setting up tests.
2. Currently the test setDescription() test only veri\xef\xac\x81es that None is not allowed as input
value.
(a) Improve the test, so that all other builtin types are tested as well.
(b) Also, make sure that any objects inheriting from str or unicode pass as valid values.

\n+Chapter 44
Writing Basic Unit Tests
Di\xef\xac\x83culty
Newcomer
Skills
\xe2\x80\xa2 All you need to know is some Python.
Problem/Task
As you know by now, Zope 3 gains its incredible stability from testing any code in great detail. The
currently most common method is to write unit tests. This chapter introduces unit tests \xe2\x80\x93 which
are Zope 3 independent \xe2\x80\x93 and introduces some of the subtleties.
Solution
44.1
Implementing the Sample Class
Before we can write tests, we have to write some code that we can test. Here, we will implement
a simple class called Sample with a public attribute title and description that is accessed
via getDescription() and mutated using setDescription(). Further, the description must be
either a regular or unicode string.
Since this code will not depend on Zope, open a \xef\xac\x81le named test sample.py anywhere and add
the following class:
1 Sample(object):
2
"""A trivial Sample object."""
3
4
title = None
5
6
def __init__(self):
7
"""Initialize object."""
8
self._description = \xe2\x80\x99\xe2\x80\x99
9
1

2
CHAPTER 44. WRITING BASIC UNIT TESTS
10
def setDescription(self, value):
11
"""Change the value of the description."""
12
assert isinstance(value, (str, unicode))
13
self._description = value
14
15
def getDescription(self):
16
"""Change the value of the description."""
17
return self._description
Line 4: The title is just publicly declared and a value of None is given. Therefore this is just
a regular attribute.
Line 8: The actual description string will be stored in description.
Line 12: Make sure that the description is only a regular or unicode string, like it was stated in
the requirements.
If you wish you can now manually test the class with the interactive Python shell. Just start
Python by entering python in your shell prompt. Note that you should be in the directory in
which test sample.py is located when starting Python (an alternative is of course to specify the
directory in your PYTHONPATH.)
1 >>> from test_sample import Sample
2 >>> sample = Sample()
3 >>> print sample.title
4 None
5 >>> sample.title = \xe2\x80\x99Title\xe2\x80\x99
6 >>> print sample.title
7 Title
8 >>> print sample.getDescription()
9
10 >>> sample.setDescription(\xe2\x80\x99Hello World\xe2\x80\x99)
11 >>> print sample.getDescription()
12 Hello World
13 >>> sample.setDescription(None)
14 Traceback (most recent call last):
15
File "<stdin>", line 1, in ?
16
File "test_sample.py", line 31, in setDescription
17
assert isinstance(value, (str, unicode))
18 AssertionError
As you can see in the last test, non-string object types are not allowed as descriptions and an
AssertionError is raised.
44.2
Writing the Unit Tests
The goal of writing the unit tests is to convert this informal, manual, and interactive testing session
into a formal test class. Python provides already a module called unittest for this purpose, which
is a port of the Java-based unit testing product, JUnit, by Kent Beck and Erich Gamma. There are
three levels to the testing framework (this list deviates a bit from the original de\xef\xac\x81nitions as found
in the Python library documentation. 1).
1 http://www.python.org/doc/current/lib/module-unittest.html

44.2. WRITING THE UNIT TESTS
3
The smallest unit is obviously the \xe2\x80\x9ctest\xe2\x80\x9d, which is a single method in a TestCase class that
tests the behavior of a small piece of code or a particular aspect of an implementation. The \xe2\x80\x9ctest
case\xe2\x80\x9d is then a collection tests that share the same setup/inputs. On top of all of this sits the \xe2\x80\x9ctest
suite\xe2\x80\x9d which is a collection of test cases and/or other test suites. Test suites combine tests that
should be executed together. With the correct setup (as shown in the example below), you can
then execute test suites. For large projects like Zope 3, it is useful to know that there is also the
concept of a test runner, which manages the test run of all or a set of tests. The runner provides
useful feedback to the application, so that various user interfaces can be developed on top of it.
But enough about the theory. In the following example, which you can simply put into the same
\xef\xac\x81le as your code above, you will see a test in common Zope 3 style.
1 import unittest
2
3 class SampleTest(unittest.TestCase):
4
"""Test the Sample class"""
5
6
def test_title(self):
7
sample = Sample()
8
self.assertEqual(sample.title, None)
9
sample.title = \xe2\x80\x99Sample Title\xe2\x80\x99
10
self.assertEqual(sample.title, \xe2\x80\x99Sample Title\xe2\x80\x99)
11
12
def test_getDescription(self):
13
sample = Sample()
14
self.assertEqual(sample.getDescription(), \xe2\x80\x99\xe2\x80\x99)
15
sample._description = "Description"
16
self.assertEqual(sample.getDescription(), \xe2\x80\x99Description\xe2\x80\x99)
17
18
def test_setDescription(self):
19
sample = Sample()
20
self.assertEqual(sample._description, \xe2\x80\x99\xe2\x80\x99)
21
sample.setDescription(\xe2\x80\x99Description\xe2\x80\x99)
22
self.assertEqual(sample._description, \xe2\x80\x99Description\xe2\x80\x99)
23
sample.setDescription(u\xe2\x80\x99Description2\xe2\x80\x99)
24
self.assertEqual(sample._description, u\xe2\x80\x99Description2\xe2\x80\x99)
25
self.assertRaises(AssertionError, sample.setDescription, None)
26
27
28 def test_suite():
29
return unittest.TestSuite((
30
unittest.makeSuite(SampleTest),
31
))
32
33 if __name__ == \xe2\x80\x99__main__\xe2\x80\x99:
34
unittest.main(defaultTest=\xe2\x80\x99test_suite\xe2\x80\x99)
Line 3\xe2\x80\x934: We usually develop test classes which must inherit from TestCase. While often not
done, it is a good idea to give the class a meaningful docstring that describes the purpose of the
tests it includes.
Line 6, 12 & 18: When a test case is run, a method called runTests() is executed. While it
is possible to override this method to run tests di\xef\xac\x80erently, the default option will look for any
method whose name starts with test and execute it as a single test. This way we can create
a \xe2\x80\x9ctest method\xe2\x80\x9d for each aspect, method, function or property of the code to be tested. This
default is very sensible and is used everywhere in Zope 3.

4
CHAPTER 44. WRITING BASIC UNIT TESTS
Note that there is no docstring for test methods. This is intentional. If a docstring is speci\xef\xac\x81ed,
it is used instead of the method name to identify the test. When specifying a docstring, we have
noticed that it is very di\xef\xac\x83cult to identify the test later; therefore the method name is a much
better choice.
Line 8, 10, 14, . . . : The TestCase class implements a handful of methods that aid you with the
testing. Here are some of the most frequently used ones. For a complete list see the standard
Python documentation referenced above.
\xe2\x80\xa2 assertEqual(first,second[,msg])
Checks whether the first and second value are equal. If the test fails, the msg or None
is returned.
\xe2\x80\xa2 assertNotEqual(first,second[,msg])
This is simply the opposite to assertEqual() by checking for non-equality.
\xe2\x80\xa2 assertRaises(exception,callable,...)
You expect the callable to raise exception when executed. After the callable you can
specify any amount of positional and keyword arguments for the callable. If you expect
a group of exceptions from the execution, you can make exception a tuple of possible
exceptions.
\xe2\x80\xa2 assert (expr[,msg])
Assert checks whether the speci\xef\xac\x81ed expression executes correctly. If not, the test fails and
msg or None is returned.
\xe2\x80\xa2 assertEqual()
This testing method is equivalent to assertEqual().
\xe2\x80\xa2 assertTrue(expr[,msg])
This method is equivalent to assert (expr[,msg]).
\xe2\x80\xa2 assertFalse()
This is the opposite to assertTrue().
\xe2\x80\xa2 fail([msg])
Fails the running test without any evaluation. This is commonly used when testing various
possible execution paths at once and you would like to signify a failure if an improper path
was taken.
Line 6\xe2\x80\x9310: This method tests the title attribute of the Sample class. The \xef\xac\x81rst test should
be of course that the attribute exists and has the expected initial value (line 8). Then the title
attribute is changed and we check whether the value was really stored. This might seem like
overkill, but later you might change the title in a way that it uses properties instead. Then it
becomes very important to check whether this test still passes.
Line 12\xe2\x80\x9316: First we simply check that getDescription() returns the correct default value.
Since we do not want to use other API calls like setDescription() we set a new value of the
description via the implementation-internal description attribute (line 15). This is okay! Unit
tests can make use of implementation-speci\xef\xac\x81c attributes and methods. Finally we just check that
the correct value is returned.

44.3. RUNNING THE TESTS
5
Line 18\xe2\x80\x9325: On line 21\xe2\x80\x9324 it is checked that both regular and unicode strings are set correctly.
In the last line of the test we make sure that no other type of objects can be set as a description
and that an error is raised.
28\xe2\x80\x9331: This method returns a test suite that includes all test cases created in this module. It is
used by the Zope 3 test runner when it picks up all available tests. You would basically add the
line unittest.makeSuite(TestCaseClass) for each additional test case.
33\xe2\x80\x9334: In order to make the test module runnable by itself, you can execute unittest.main()
when the module is run.
44.3
Running the Tests
You can run the test by simply calling pythontest sample.py from the directory you saved the
\xef\xac\x81le in. Here is the result you should see:
.
--------------------------------------------------------------------
n 3 tests in 0.001s
The three dots represent the three tests that were run. If a test had failed, it would have been
reported pointing out the failing test and providing a small traceback.
When using the default Zope 3 test runner, tests will be picked up as long as they follow some
conventions.
\xe2\x80\xa2 The tests must either be in a package or be a module called tests.
\xe2\x80\xa2 If tests is a package, then all test modules inside must also have a name starting with test,
as it is the case with our name test sample.py.
\xe2\x80\xa2 The test module must be somewhere in the Zope 3 source tree, since the test runner looks
only for \xef\xac\x81les there.
In our case, you could simply create a tests package in ZOPE3/src (do not forget the
init .
py \xef\xac\x81le). Then place the test sample.py \xef\xac\x81le into this directory.
You you can use the test runner to run only the sample tests as follows from the Zope 3 root
directory:
python test.py -vp tests.test_sample
The -v option stands for verbose mode, so that detailed information about a test failure is
provided. The -p option enables a progress bar that tells you how many tests out of all have been
completed. There are many more options that can be speci\xef\xac\x81ed. You can get a full list of them with
the option -h: pythontest.py-h.
The output of the call above is as follows:
nfiguration file found.
nning UNIT tests at level 1
nning UNIT tests from /opt/zope/Zope3
3/3 (100.0%): test_title (tests.test_sample.SampleTest)
--------------------------------------------------------------------
n 3 tests in 0.002s

6
CHAPTER 44. WRITING BASIC UNIT TESTS
nning FUNCTIONAL tests at level 1
nning FUNCTIONAL tests from /opt/zope/Zope3
--------------------------------------------------------------------
n 0 tests in 0.000s
Line 1: The test runner uses a con\xef\xac\x81guration \xef\xac\x81le for some setup. This allows developers to use
the test runner for other projects as well. This message simply tells us that the con\xef\xac\x81guration \xef\xac\x81le
was found.
Line 2\xe2\x80\x938: The unit tests are run. On line 4 you can see the progress bar.
Line 9\xe2\x80\x9315: The functional tests are run, since the default test runner runs both types of tests.
Since we do not have any functional tests in the speci\xef\xac\x81ed module, there are no tests to run. To
just run the unit tests, use option -u and -f for just running the functional tests. See \xe2\x80\x9cWriting
Functional Tests\xe2\x80\x9d for more details on functional tests.

44.3. RUNNING THE TESTS
7
Exercises
1. It is not very common to do the setup \xe2\x80\x93 in our case sample=Sample() \xe2\x80\x93 in every test
method. Instead there exists a method called setUp() and its counterpart tearDown that
are run before and after each test, respectively. Change the test code above, so that it uses
the setUp() method. In later chapters and the rest of the book we will frequently use this
method of setting up tests.
2. Currently the test setDescription() test only veri\xef\xac\x81es that None is not allowed as input
value.
(a) Improve the test, so that all other builtin types are tested as well.
(b) Also, make sure that any objects inheriting from str or unicode pass as valid values.

\n' +b'diff --git a/news/248.bugfix b/news/248.bugfix\nnew file mode 100644\nindex 00000000..1feb6363\n--- /dev/null\n+++ b/news/248.bugfix\n@@ -0,0 +1 @@\n+Update the link and label under Site Setup > Theming > Advanced settings > Custom Styles to Theming of Classic UI at https://6.docs.plone.org/classic-ui/theming/index.html. @stevepiercy\n' -Repository: Products.PortalTransforms +Repository: plone.app.theming Branch: refs/heads/master -Date: 2024-11-29T14:35:03-03:00 -Author: Maurits van Rees (mauritsvanrees) -Commit: https://github.com/plone/Products.PortalTransforms/commit/3616e70342f03f4bc985f95c12e26e9cac5a3f49 +Date: 2024-12-04T06:32:57-08:00 +Author: Steve Piercy (stevepiercy) +Commit: https://github.com/plone/plone.app.theming/commit/907d61d66eab7c649ed0b2f5c02e3dd3e57e319d -Merge pull request #70 from plone/py-3.13-unittest +Merge pull request #248 from plone/docs-classic-ui-theming -Fix removed `unittest.makeSuite` in python 3.13 +Update link and label to Theming of Classic UI documentation Files changed: -A news/70.bugfix -M Products/PortalTransforms/tests/test_intelligenttext.py -M Products/PortalTransforms/tests/test_transforms.py -M docs/dev_manual.rst +A news/248.bugfix +M src/plone/app/theming/browser/controlpanel.pt -b'diff --git a/Products/PortalTransforms/tests/test_intelligenttext.py b/Products/PortalTransforms/tests/test_intelligenttext.py\nindex 77ddb53..63dc934 100644\n--- a/Products/PortalTransforms/tests/test_intelligenttext.py\n+++ b/Products/PortalTransforms/tests/test_intelligenttext.py\n@@ -155,10 +155,11 @@ def testWhitespace(self):\n \n \n def test_suite():\n- from unittest import makeSuite\n- from unittest import TestSuite\n+ import unittest\n \n- suite = TestSuite()\n- suite.addTest(makeSuite(TestIntelligentTextToHtml))\n- suite.addTest(makeSuite(TestHtmlToIntelligentText))\n- return suite\n+ return unittest.TestSuite(\n+ (\n+ unittest.defaultTestLoader.loadTestsFromTestCase(TestIntelligentTextToHtml),\n+ unittest.defaultTestLoader.loadTestsFromTestCase(TestHtmlToIntelligentText),\n+ )\n+ )\ndiff --git a/Products/PortalTransforms/tests/test_transforms.py b/Products/PortalTransforms/tests/test_transforms.py\nindex e8c24e5..08d135a 100644\n--- a/Products/PortalTransforms/tests/test_transforms.py\n+++ b/Products/PortalTransforms/tests/test_transforms.py\n@@ -714,10 +714,9 @@ class TransformTestSubclass(TransformTest):\n \n \n def test_suite():\n- from unittest import makeSuite\n- from unittest import TestSuite\n+ import unittest\n \n- suite = TestSuite()\n+ suite = unittest.TestSuite()\n for test in make_tests():\n- suite.addTest(makeSuite(test))\n+ suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(test))\n return suite\ndiff --git a/docs/dev_manual.rst b/docs/dev_manual.rst\nindex be8e716..11d0c05 100644\n--- a/docs/dev_manual.rst\n+++ b/docs/dev_manual.rst\n@@ -92,7 +92,7 @@ Writing a new transformation\n ============================\n Writing a new transform should be an easy task. You only have to follow a\n simple interface to do it, but knowing some advanced features and provided\n-utilities may help to do it quicker... \n+utilities may help to do it quicker...\n \n \n Related interfaces\n@@ -182,16 +182,16 @@ Transform utilities may be found in the libtransforms subpackage. You\'ll find\n there the following modules :\n \n *commandtransform*\n- provides a base class for external command based transforms. \n+ provides a base class for external command based transforms.\n \n *retransform*\n- provides a base class for regular expression based transforms. \n+ provides a base class for regular expression based transforms.\n \n *html4zope*\n provides a docutils HTML writer for Zope.\n \n *utils*\n- provides some utilities functions. \n+ provides some utilities functions.\n \n \n Write a test your transform!\n@@ -202,11 +202,11 @@ cola into beer (I let you find MIME type for these content types ;). Basically,\n your test file should be::\n \n from test_transforms import make_tests\n- \n+\n tests =(\'Products.MyTransforms.colabeer\', "drink.cola", "drink.beer", None, 0)\n \n def test_suite():\n- return TestSuite([makeSuite(test) for test in make_tests()])\n+ return TestSuite([unittest.defaultTestLoader.loadTestsFromTestCase(test) for test in make_tests()])\n \n if __name__==\'__main__\':\n main(defaultTest=\'test_suite\')\n@@ -216,10 +216,10 @@ your test file should be::\n In this example:\n \n - "Products.MyTransforms.colabeer" is the module defining your transform (you\n- can also give directly the transform instance). \n+ can also give directly the transform instance).\n \n - "drink.cola" is the name of the file containing data to give to your transform\n- as input. \n+ as input.\n \n - "drink.beer" is the file containing expected transform result (what the getData\n method of idatastream will return).\ndiff --git a/news/70.bugfix b/news/70.bugfix\nnew file mode 100644\nindex 0000000..da7158a\n--- /dev/null\n+++ b/news/70.bugfix\n@@ -0,0 +1,2 @@\n+Fix removed `unittest.makeSuite` in python 3.13\n+[petschki]\n' +b'diff --git a/news/248.bugfix b/news/248.bugfix\nnew file mode 100644\nindex 00000000..1feb6363\n--- /dev/null\n+++ b/news/248.bugfix\n@@ -0,0 +1 @@\n+Update the link and label under Site Setup > Theming > Advanced settings > Custom Styles to Theming of Classic UI at https://6.docs.plone.org/classic-ui/theming/index.html. @stevepiercy\ndiff --git a/src/plone/app/theming/browser/controlpanel.pt b/src/plone/app/theming/browser/controlpanel.pt\nindex 382f638b..621c8d5d 100644\n--- a/src/plone/app/theming/browser/controlpanel.pt\n+++ b/src/plone/app/theming/browser/controlpanel.pt\n@@ -725,10 +725,10 @@\n Define your own custom CSS in the field below. This is a good place for quick customizations of things like colors and the toolbar. Definitions here will override previously defined CSS of Plone. Please use this only for small customizations, as it is hard to keep track of changes here. For bigger changes you most likely want to customize a full theme and make your changes there.\n
\n \n \n