Author: maikroeder
Date: Wed Mar 22 01:57:05 2006
New Revision: 2682


Modified: cookbook/trunk/recipe07.en.tex
--- cookbook/trunk/recipe07.en.tex      (original)
+++ cookbook/trunk/recipe07.en.tex      Wed Mar 22 01:57:05 2006
@@ -3,17 +3,252 @@
 \author{Tarek Ziad�}
+\translator{Maik R�der}
 \chapter*{Writing functional tests}
-Not translated in english yet.
+Functional or "acceptance tests" are complementary to unit
+tests: They are black boxes that make sure that the
+functionality of the whole system, is working as it was
+These tests are only covering the use of the system from the
+point of view of a normal user, in order to make sure that
+all the expected functionalities are present and work as
+They are an infallible sign of quality for the end users, who
+may even occasionally write or extend these tests themselves.
+In terms of proportion, a system should have approximately
+2/3 of unit tests and 1/3 functional tests, given that the
+latter are on a higher level.
+For web applications, these tests need a special execution
+environment, which imitates a browser as best as possible, and
+offers an API for lauching requests and study the response, 
+just like it would be done by FireFox or Internet Explorer.
-Not translated in english yet.
+\section*{Understanding how it works}
+By definition, a functional tests only concentrates on the
+functionality of the application, and doesn't care for 
+implementation details, as opposed to unit tests. It is a
+practical test as performed by an end user, who can for example
+browse the menues of the application in order to test each
+of them. He then uses a check list to make sure that all expected
+functionality is there and works fine. The system is said to
+be "validated" once all items in the check are verified to be
+correct and in conformity with the specification.
+Functional tests for Python and of course for Zope, are done
+by the means of test classes derived from unit test classes, or
+in doctests.
+The only difference between unit tests and functional tests
+is the interface between the system and the test writer.
+The tester writer has to write the tests under the same
+conditions that an end user would have at their disposal.
+\subsection*{Writing functional tests with classes}
+Zope provides a special module in the \code{}
+package called \code{functional}, whose classes inherit from
+\code{unittest.TestCase}. The most commonly used class is
+\noindent It has the following methods:
+\item \code{getRootFolder()}: returns the Zope root folder. It's used
+to prepare the site before analysing a publication.
+\item \code{publish(path, basic=None)}: Returns the rendered object
+behind the path in the form of a \code{Response} object. the 
+optional \code{basic} parameter has the form
+\code{'login:password'}. The string 'mgr:mgrpw' can be used to 
+authenticate with manager rights.
+\item \code{checkForBrokenLinks(body,path, basic=None)}: Checks for
+broken links in the \code{body}, which is a string representing 
+the body of the response (fetched with \code{Response.getBody()}).
+\code{path} gives the relative path that corresponds to the object
+at the origin of the response. This parameter is needed by the
+method in order to make sense of relative links in the site.
+The test utility augments the \code{Response} object with 
+methods for retrieving information easily, such as:
+\item \code{getBody()}: returns the response body
+\item \code{getOutput()}: returns the complete response (body and 
+\item \code{getStatus()}: returns the status
+\item \code{getHeaders()}: for retrieving the headers
+In the following example, a folder is created under the
+root folder, and its rendering is verified:
+\codetitle{Verification classof the rendering of a folder:}
+>>> import transaction
+>>> from import functional
+>>> from buddydemo.buddy import BuddyFolder
+>>> class FolderTest(functional.BrowserTestCase):
+...     def test_folder(self):
+...         root = self.getRootFolder()
+...         # create a Folder
+...         root['cool_folder'] = BuddyFolder()
+...         transaction.commit()
+...         # get the root content through the publisher
+...         response = self.publish('/cool_folder', basic='mgr:mgrpw')
+...         self.assert_('<title>Z3: cool_folder</title>' in
+...                      response.getBody())
+\item After having validated the modification in the site
+with the code \code{transaction.commit()}, the object is
+available for the \code{publish} call.
+The test is executed like a classic unit test.
+\codetitle{Starting the test:}
+>>> import unittest
+>>> suite = unittest.makeSuite(FolderTest)
+>>> test_runner = unittest.TextTestRunner()
+<unittest._TextTestResult run=1 errors=0 failures=0>
+\subsection*{Functionaltests with doctests}
+Functional tests are always very high level tests, and come very
+close to example code that could be part of a documentation. In
+Python, the both the need for documentation and for testing can
+be satisfied by the means of doctests.
+\subsubsection*{Understanding Python doctests}
+The idea of putting tests into Python doctests is quite intriguing:
+The code is written in the docstrings as a series of interactions
+on the Python prompt.
+\codetitle{Doctest example:}
+>>> def theTruth():
+...     """
+...     >>> theTruth()
+...     'doctests rule'
+...     """
+...     return 'doctests rule'
+The Python \code{doctest} module contains utilities for extracting
+and execuring these tests.
+\codetitle{Execution of a doctest:}
+>>> import doctest
+>>> doctest.run_docstring_examples(theTruth, globals(), verbose=True)
+Finding tests in NoName
+    theTruth()
+    'doctests rule'
+Once doctests are added to all modules, classes and methods, the
+readability of the code may suffer. In order to avoid this code
+obfuscation, it is recommended to group the functional tests
+in a separate file, which is then "executed". This opens the
+way for explaining the examples in more depth, and to follow
+logical steps. In this case, we can speak of "agile documentation",
+or "executable documentation".
+The documents can then be called by the test module.
+\codetitle{Agile document:}
+Le module of truth
+This document doesn't contain any functional tests, but
+a unit test.
+It doesn't matter at all.
+The truth module contains the function `theTruth()`, which
+can be called for showing a message. This method doesn't serve
+any real purpose.
+Usage example::
+  >>> from truth import theTruth
+  >>> theTruth()
+  'doctests rule'
+\subsubsection*{Understanding functional doctests in Zope}
+In order to easily manipulate doctests in Zope, the
+\code{zope.testbrowser} package offers the  \code{Browser} class,
+which allows the simulation of a browser and its functionalities.
+By convention, this class is invoked with the URL starting with
+\url{http://localhost}, followed by the path to the Zope server.
+This is how you can write a test to reach the page situated at the
+>>> from zope.testbrowser.testing import Browser
+>>> browser = Browser('http://localhost')
+>>> browser.url
+>>> browser.contents
+'...<title>Z3: </title>...'
+\item The \code{contents} property is used to fetch the content of
+the page in the form of a string.
+\item The '...' (or ellipsis) have a precise meaning in the doctests:
+They stand for an arbitrary amount of text. In the above example,
+they allow us to make sure that \code{contents} does contain the
+given HTMLcode.
+Besides \code{contents} and \code{url}, the main elements provided by
+\code{Browser} are:
+\item \code{open(url)}: allows to open the given URL.
+\item \code{isHtml}: is the content written in HTML ?
+\item \code{title}: returns the title of the page.
+\item \code{headers}: returns the Python object \code{httplib.HTTPMessage}
+containing the headers.
+\item \code{getLink(text)} : fetches the \code{Link} object for the link
+given in \code{text}. This link can then be followed using the \code{click()}
+method, whereby the new pageis loaded in the \code{Browser} instance.
+You will be able to learn about further elements offered by this
+utility in some other recipes.
+\section*{Limitations of functional tests}
+Functional tests for the web are somewhat limited in what they can do if
+compared to browers: It is impossible to test JavaScript contained in the
+pages, because each browser comes with its own engine. For this, you need
+to look at other technologies, like \textit{Selenium}, which runs the tests
+inside a real browser.

Reply via email to