[Checkins] SVN: zdgbook/trunk/ stx to rst

Baiju M baiju.m.mail at gmail.com
Wed Feb 18 00:11:19 EST 2009


Log message for revision 96663:
  stx to rst
  

Changed:
  D   zdgbook/trunk/TestingAndDebugging.stx
  A   zdgbook/trunk/source/TestingAndDebugging.rst
  U   zdgbook/trunk/source/index.rst

-=-
Deleted: zdgbook/trunk/TestingAndDebugging.stx
===================================================================
--- zdgbook/trunk/TestingAndDebugging.stx	2009-02-18 04:48:42 UTC (rev 96662)
+++ zdgbook/trunk/TestingAndDebugging.stx	2009-02-18 05:11:19 UTC (rev 96663)
@@ -1,946 +0,0 @@
-Chapter 7: Testing and Debugging
-
-  As you develop Zope applications you will run into problems. This
-  chapter covers debugging and testing techniques that can help
-  you. The Zope debugger allow you to peek inside a running process
-  and find exactly what is going wrong. Unit testing allows you to
-  automate the testing process to ensure that your code still works
-  correctly as you change it. Finally, Zope provides logging
-  facilities which allow you to emit warnings and error messages.
-
-    % Anonymous User - Jan. 24, 2002 7:01 am - (This comment does not really fit here but I think it is important)  Use the source!    If you want to know how a function in Zope doesn't do what it should  try to find it: "cd $ZOPE_HOME/lib/python; grep -irs "def foo" *    Windows Users: grep is included in cygwin (http://www.cygwin.com/)
-
-    % Anonymous User - Jan. 8, 2003 11:50 pm:
-     rwar
-
-  Debugging
-
-    Zope provides debugging information through a number of
-    sources. It also allows you a couple avenues for getting
-    information about Zope as it runs.
-
-    The Control Panel
-
-      The control panel provides a number of views that can help you
-      debug Zope, especially in the area of performance.  The
-      *Debugging Information* link on the control panel provides two
-      views, *Debugging Info* and *Profiling*.
-
-        % Anonymous User - May 8, 2005 11:50 pm:
-         Whee!
-
-      Debugging info provides information on the number of object
-      references and the status of open requests.  The object
-      references list displays the name of the object and the number
-      of references to that object in Zope.  Understanding how
-      reference counts help debugging is a lengthy subject, but in
-      general you can spot memory leaks in your application if the
-      number of references to certain objects increases without bound.
-      The busier your site is, or the more content it holds, the more
-      reference counts you will tend to have.
-
-      Profiling uses the standard Python profiler.  This is turned on
-      by setting the 'PROFILE_PUBLISHER' environment variable before
-      executing Zope.
-
-        % Anonymous User - Mar. 11, 2003 5:31 am:
-         It doesn't work for me :(
-         In ZOPE_HOME/start i set 
-         export PROFILE_PUBLISHER="/usr/local/zope/profile.out"
-         exec /usr/local/bin/python $cwd/z2.py -D -u flo "$@"
-         but i don't get any change in the profiling tab
-
-        % Anonymous User - Mar. 17, 2003 4:22 pm:
-         It's ok now. - I'm not sure what my mistake was.
-
-      When the profiler is running, the performance of your Zope
-      system will suffer a lot.  Profiling should only be used for
-      short periods of time, or on a separate ZEO client so that your
-      normal users to not experience this significant penalty.
-
-        % Anonymous User - Jan. 28, 2002 4:01 pm - typo /users to not/users do not/
-
-      Profiling provides you with information about which methods in
-      your Zope system are taking the most time to execute.  It builds
-      a *profile*, which lists the busiest methods on your system,
-      sorted by increasing resource usage.  For details on the meaning
-      of the profiler's output, read the "standard Python
-      documentation":http://www.python.org/doc/current/lib/profile.html.
-
-        % Anonymous User - Nov. 3, 2004 7:22 am:
-         testing
-
-    Product Refresh Settings
-
-      As of Zope 2.4 there is a *Refresh* view on all Control Panel
-      Products. Refresh allows you to reload your product's modules as
-      you change them, rather than having to restart Zope to see your
-      changes. The *Refresh* view provides the same debugging
-      functionality previously provided by Shane Hathaway's Refresh
-      Product.
-
-      To turn on product refresh capabilities place a 'refresh.txt'
-      file in your product's directory. Then visit the *Refresh* view
-      of your product in the management interface. Here you can
-      manually reload your product's modules with the *Refresh this
-      product* button. This allows you to immediately see the effect
-      of your changes, without restarting Zope. You can also turn on
-      automatic refreshing which causes Zope to frequently check for
-      changes to your modules and refresh your product when it detects
-      that your files have changed. Since automatic refresh causes
-      Zope to run more slowly, it is a good idea to only turn it on
-      for a few products at a time.
-
-        % Anonymous User - Nov. 16, 2002 4:04 am:
-         Does refresh.txt require any content, or is just the presence of the file enough to work the 'magic'?
-
-        % Anonymous User - Nov. 16, 2002 4:04 am:
-         And just how do you turn on (and off) automatic refreshing?
-
-        % Anonymous User - Nov. 28, 2002 11:39 am:
-         I just found the answer to your question here:
-         http://www.zope.org/Members/dylanr/prod_tips
-         It must be an empty file. Anyway, I agree that they should be more specific. About your second question, I
-         also don't have any idea about how does it work.
-
-        % Anonymous User - Nov. 30, 2002 1:36 pm:
-         Doesn't work for me (Zope 2.5.1) using the Poll Product from the earlier in the book. Neither explicit
-         refreshes of the product nor the refresh.txt mechanism appear to reload changed code. I need to restart zope
-         before changes take effect.
-
-        % Anonymous User - Jan. 28, 2003 2:59 am:
-         I also have to restart Zope before changes take effect. (2.6.0)
-
-        % to_be - May 8, 2003 7:00 am:
-         An empty refresh.txt is sufficient, but if there is text in it, this will be displayed as 'Important
-         information about refreshing this product' (Zope 2.6.1)
-
-    Debug Mode
-
-      Setting the 'Z_DEBUG_MODE=1' environment puts Zope into debug
-      mode.  This mode reduces the performance of Zope a little bit.
-      Debug model has a number of wide ranging effects:
-
-        o Tracebacks are shown on the browser when errors are raised.
-
-        o External Methods and DTMLFile objects are checked to see if
-          they have been modified every time they are called.  If
-          modified, they are reloaded.
-
-        o Zope will not fork into the background in debug mode,
-          instead, it will remain attached to the terminal that
-          started it and the main logging information will be
-          redirected to that terminal.
-
-      Normally, debug mode is set using the '-D' switch when starting
-      Zope, though you can set the environment variable directly if
-      you wish.
-
-        % Anonymous User - Nov. 17, 2001 11:07 pm - In the source directory of your zope installation your start file should look something like this<br>  #! /bin/sh<br>  reldir=`dirname $0`<br>  PYTHONHOME=`cd $reldir; pwd`<br>  export PYTHONHOME<br>  exec /usr/bin/python \<br>       $PYTHONHOME/z2.py \<br>       -D "$@"<br>  <br>You will also notice that when running from the command line that the shell isn't returned to a command mode, this is normal.<br><br>
-
-      By using debug mode and product refresh together you will have
-      little reason to restart Zope while developing.
-
-    The Python Debugger
-
-      Zope is integrated with the Python debugger (pdb).  The Python
-      debugger is pretty simple as command line debuggers go, and
-      anyone familiar with other popular command line debuggers (like
-      gdb) will feel right at home in pdb.
-
-      For an introduction to pdb see the standard "pdb
-      documentation":http://www.python.org/doc/current/lib/module-pdb.html.
-
-      There are a number of ways to debug a
-      Zope process:
-
-        o You can shut down the Zope server and simulate a request on the
-          command line.
-
-        o You can run a special ZEO client that debugs a running server.
-
-        o You can run Zope in debug model and enter the debugger
-          through Zope's terminal session.
-
-      The first method is an easy way to debug Zope if you are not
-      running ZEO.  First, you must first shut down the Zope process.
-      It is not possible to debug Zope in this way and run it at the
-      same time.  Starting up the debugger this way will by default
-      start Zope in single threaded mode.
-
-        % Anonymous User - Jan. 25, 2002 9:25 am - Typo: debug modeL
-
-        % Anonymous User - July 6, 2002 7:08 am:
-         Typo: first you must first
-
-      For most Zope developer's purposes, the debugger is needed to
-      debug some sort of application level programming error.  A
-      common scenario is when developing a new product for Zope.
-      Products extend Zope's functionality but they also present the
-      same kind of debugging problems that are commonly found in any
-      programming environment.  It is useful to have an existing
-      debugging infrastructure to help you jump immediately to your
-      new object and debug it and play with it directly in pdb.  The
-      Zope debugger lets you do this.
-
-      In reality, the "Zope" part of the Zope debugger is actually
-      just a handy way to start up Zope with some pre-configured break
-      points and to tell the Python debugger where in Zope you want to
-      start debugging.
-
-      Simulating HTTP Requests
-
-        Now for an example. Remember, for this example to work, you
-        *must* shut down Zope. Go to your Zope's 'lib/python' directory
-        and fire up Python and import 'Zope' and 'ZPublisher'::
-
-          $ cd lib/python
-          $ python
-          Python 1.5.2 (#0, Apr 13 1999, 10:51:12) [MSC 32 bit (Intel)] on win32
-          Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
-          >>> import Zope, ZPublisher
-          >>>
-
-        Here you have run the Python interpreter (which is where using the
-        debugger takes place) and imported two modules, 'Zope' and
-        'ZPublisher'.  If Python complains about an 'ImportError' and not
-        being able to find either module, then you are probably in the wrong
-        directory, or you have not compiled Zope properly. If you get
-        this message::
-
-          ZODB.POSException.StorageSystemError: Could not lock the
-          database file. There must be another process that has opened
-          the file.
-
-        This tells you that Zope is currently running. Shutdown Zope and
-        try again.
-
-        The 'Zope' module is the main Zope application module. When you
-        import 'Zope' it sets up Zope. 'ZPublisher' is the Zope ORB. See
-        Chapter 2 for more information about 'ZPublisher'.
-
-        You can use the 'ZPublisher.Zope' function to simulate an HTTP
-        request. Pass the function a URL relative the your root Zope
-        object. Here is an example of how to simulate an HTTP request
-        from the debugger::
-
-          >>> ZPublisher.Zope('')
-          Status: 200 OK
-          X-Powered-By: Zope (www.zope.org), Python (www.python.org)
-          Content-Length: 1238
-          Content-Type: text/html
-
-          <HTML><HEAD><TITLE>Zope</TITLE>
-
-            ... blah blah...
-
-          </BODY></HTML>
-          >>> 
-
-          % Anonymous User - Nov. 22, 2002 8:42 am:
-           Can I use ZPublisher.Zope() as a link between an HTTP server and the ZPublisher machinery? I need to bypass
-           the ZServer and make Apache pass a web request directly to ZPublisher. Any howtos/docs available? Thanks
-
-        If you look closely, you will see that the content returned is
-        *exactly* what is returned when you call your root level object
-        through HTTP, including all the HTTP headers.
-
-        Keep in mind that calling Zope this way does NOT involve
-        a web server.  No ports are opened, the 'ZServer' code is not even
-        imported.  In fact, this is just an interpreter front end to
-        the same application code the ZServer *does* call.
-
-      Interactive Debugging
-
-        Debugging involves publishing a request up to a point where you think
-        it's failing, and then inspecting the state of your variables and
-        objects.  The easy part is the actual inspection, the hard part is
-        getting your program to stop at the right point.  
-
-        So, for the sake our example, let's say that you have a 'News'
-        object which is defined in a Zope Product called 'ZopeNews', and
-        is located in the 'lib/python/Products/ZopeNews' directory.  The
-        class that defines the 'News' instance is also called 'News',
-        and is defined in the 'News.py' module in your product.
-
-          % Anonymous User - Mar. 9, 2003 2:00 pm:
-           What exactly is a News object and how was it defined? Is it a class that was registered with the registrar
-           object? <a
-           href="http://www.zope.org/Documentation/Books/ZDG/current/Products.stx">http://www.zope.org/Documentation/Boo
-           ks/ZDG/current/Products.stx</a>
-           Object is to ambiguous of a term.
-
-        Therefore, from Zope's perspective the fully qualified name of
-        your class is 'Products.ZopeNews.News.News'.  All Zope objects
-        have this kind of fully qualified name.  For example, the
-        'ZCatalog' class can be found in
-        'Products.ZCatalog.ZCatalog.ZCatalog' (The redundancy is because
-        the product, module, and class are all named 'ZCatalog').
-
-        Now let's create an example method to debug.  You want your news
-        object to have a 'postnews' method, that posts news::
-
-          class News(...):
-
-              ...
-
-              def postnews(self, news, author="Anonymous"):
-                  self.news = news
-
-              def quote(self):
-                  return '%s said, "%s"' % (self.author, self.news)
-
-        You may notice that there's something wrong with the 'postnews'
-        method.  The method assigns 'news' to an instance variable, but
-        it does nothing with 'author'.  If the 'quote' method is called,
-        it will raise an 'AttributeError' when it tries to look up the
-        name 'self.author'.  Although this is a pretty obvious goof,
-        we'll use it to illustrate using the debugger to fix it.
-
-        Running the debugger is done in a very similar way to how you
-        called Zope through the python interpreter before, except that
-        you introduce one new argument to the call to 'Zope'::
-
-          >>> ZPublisher.Zope('/News/postnews?new=blah', d=1)
-          * Type "s<cr>c<cr>" to jump to beginning of real publishing process.
-          * Then type c<cr> to jump to the beginning of the URL traversal
-            algorithm.
-          * Then type c<cr> to jump to published object call.
-          > <string>(0)?()
-          pdb> 
-
-        Here, you call Zope from the interpreter, just like before,
-        but there are two differences.  First, you call the 'postnews'
-        method with an argument using the URL,
-        '/News/postnews?new=blah'.  Second, you provided a new
-        argument to the Zope call, 'd=1'.  The 'd' argument, when
-        true, causes Zope to fire up in the Python debugger, pdb.
-        Notice how the Python prompt changed from '>>>' to 'pdb>'.
-        This indicates that you are in the debugger.
-
-        When you first fire up the debugger, Zope gives you a helpful
-        message that tells you how to get to your object.  To
-        understand this message, it's useful to know how you have set
-        Zope up to be debugged.  When Zope fires up in debugger mode,
-        there are three breakpoints set for you automatically (if you
-        don't know what a breakpoint is, you need to read the python
-        "debugger
-        documentation":http://www.python.org/doc/current/lib/module-pdb.html.).
-
-        The first breakpoint stops the program at the point that
-        ZPublisher (the Zope ORB) tries to publish the application
-        module (in this case, the application module is 'Zope').  The
-        second breakpoint stops the program right before ZPublisher
-        tries to traverse down the provided URL path (in this case,
-        '/News/postnews').  The third breakpoint will stop the program
-        right before ZPublisher calls the object it finds that matches
-        the URL path (in this case, the 'News' object).
-
-        So, the little blurb that comes up and tells you some keys to
-        press is telling you these things in a terse way.  Hitting 's'
-        will *step* you into the debugger, and hitting 'c' will
-        *continue* the execution of the program until it hits a
-        breakpoint.
-
-        Note however that none of these breakpoints will stop the program at
-        'postnews'.  To stop the debugger right there, you need to tell the
-        debugger to set a new breakpoint.  Why a new breakpoint?  Because
-        Zope will stop you before it traverse your objects path, it will
-        stop you before it calls the object, but if you want to stop it
-        *exactly* at some point in your code, then you have to be explicit.
-        Sometimes the first three breakpoints are convienent, but often you
-        need to set your own special break point to get you exactly where
-        you want to go.
-
-        Setting a breakpoint is easy (and see the next section for an
-        even easier method).  For example::
-
-          pdb> import Products
-          pdb> b Products.ZopeNews.News.News.postnews
-          Breakpoint 5 at C:\Program Files\WebSite\lib\python\Products\ZopeNews\News.py:42
-          pdb> 
-
-        First, you import 'Products'.  Since your module is a Zope
-        product, it can be found in the 'Products' package.  Next, you
-        set a new breakpoint with the *break* debugger command (pdb
-        allows you to use single letter commands, but you could have
-        also used the entire word 'break').  The breakpoint you set is
-        'Products.ZopeNews.News.News.postnews'.  After setting this
-        breakpoint, the debugger will respond that it found the method
-        in question in a certain file, on a certain line (in this
-        case, the fictitious line 42) and return you to the debugger.
-
-        Now, you want to get to your 'postnews' method so you can
-        start debugging it.  But along the way, you must first
-        *continue* through the various breakpoints that Zope has set
-        for you.  Although this may seem like a bit of a burden, it's
-        actually quite good to get a feel for how Zope works
-        internally by getting down the rhythm that Zope uses to
-        publish your object.  In these next examples, my 
-        comments will begin with '#".  Obviously, you won't see these
-        comments when you are debugging.  So let's debug::
-
-          pdb> s
-          # 's'tep into the actual debugging
-
-          > <string>(1)?()
-          # this is pdb's response to being stepped into, ignore it
-
-          pdb> c
-          # now, let's 'c'ontinue onto the next breakpoint
-
-          > C:\Program Files\WebSite\lib\python\ZPublisher\Publish.py(112)publish()
-          -> def publish(request, module_name, after_list, debug=0,
-
-          # pdb has stopped at the first breakpoint, which is the point where
-          # ZPubisher tries to publish the application module.
-
-          pdb> c
-          # continuing onto the next breakpoint you get...
-
-          > C:\Program Files\WebSite\lib\python\ZPublisher\Publish.py(101)call_object()
-          -> def call_object(object, args, request):
-
-        Here, 'ZPublisher' (which is now publishing the application)
-        has found your object and is about to call it.  Calling your
-        object consists of applying the arguments supplied by
-        'ZPublisher' to the object.  Here, you can see how
-        'ZPublisher' is passing three arguments into this process.
-        The first argument is 'object' and is the actual object you
-        want to call.  This can be verified by *printing* the object::
-
-          pdb> p object
-          <News instance at 00AFE410>
-
-        Now you can inspect your object (with the *print* command) and
-        even play with it a bit.  The next argument is 'args'.  This
-        is a tuple of arguments that 'ZPublisher' will apply to your
-        object call.  The final argument is 'request'.  This is the
-        request object and will eventually be transformed in to the
-        DTML usable object 'REQUEST'. Now continue, your breakpoint is
-        next::
-
-          pdb> c    
-          > C:\Program Files\WebSite\lib\python\Products\ZopeNews\News.py(42)postnews()
-          -> def postnews(self, N)
-
-        Now you are here, at your method.  To be sure, tell the
-        debugger to show you where you are in the code with the 'l'
-        command. Now you can examine variable and perform all the
-        debugging tasks that the Python debugger provides.  From here,
-        with a little knowledge of the Python debugger, you should be
-        able to do any kind of debugging task that is needed.
-
-      Interactive Debugging Triggered From the Web
-
-        If you are running in debug mode you can set break points in
-        your code and then jump straight to the debugger when Zope comes
-        across your break points. Here's how to set a breakpoint::
-
-          import pdb
-          pdb.set_trace()
-
-        Now start Zope in debug mode and point your web browser at a URL
-        that causes Zope to execute the method that includes a
-        breakpoint.  When this code is executed, the Python debugger
-        will come up in the terminal where you started Zope. Also note
-        that from your web browser it looks like Zope is frozen. Really
-        it's just waiting for you do your debugging.
-
-          % strobl - Oct. 19, 2001 2:59 am - Frankly, I don't unterstand the explaination given in the last two paragraphs at all. Zope has to be started, somehow, in order to serve requests from the web - but how? Using z2.py? Please elaborate.
-
-          % chrism - Jan. 15, 2002 11:04 pm - Start Zope normally (using "start" or "start.bat") passing in the -D switch to put it in "debug" mode, which really means "dont detach from the controlling terminal".  When Zope encounters a pdb.set_trace statement, the debugger will just "show up" on the terminal from which Zope was started.
-
-          % mreinsch - Feb. 13, 2005 5:52 am:
-           Use 'runzope'
-           You should start runzope from an emacs shell. The debugger in the emacs python-mode always jumps to the
-           current position in the source code.
-           This is also useful for non emacs users!
-           Start emacs.
-           Open a shell by 'alt-x' 'shell'.
-           Type 'PATHTORUNZOPE/runzope'
-           Sometimes emacs doesn't open the source code while debugging. Usually it helps to open the python source in
-           an extra buffer before starting the debug session.
-
-        From the terminal you are inside the debugger as it is executing
-        your request.  Be aware that you are just debugging one thread
-        in Zope, and other requests may be being served by other
-        threads.  If you go to the *Debugging Info* screen while in the
-        debugger, you can see your debugging request and how long it has
-        been open.
-
-        It is often more convenient to use this method to enter the
-        debugger than it is to call 'ZPublisher.Zope' as detailed in the
-        last section.
-
-      Post-Mortem Debugging
-
-        Often, you need to use the debugger to chase down obscure
-        problems in your code, but sometimes, the problem is obvious,
-        because an exception gets raised.  For example, consider the
-        following method on your 'News' class::
-
-          def quote(self):
-              return '%s said, "%s"' % (self.Author, self.news)
-
-        Here, you can see that the method tries to substitute
-        'self.Author' in a string, but earlier we saw that this should
-        really be 'self.author'.  If you tried to run this method from
-        the command line, an exception would be raised::
-
-          >>> ZPublisher.Zope('/News/quote')
-          Traceback (most recent call last):
-            File "<stdin>", line 1, in ?
-            File "./News.py", line 4, in test
-              test2()
-            File "./News.py", line 3, in test2
-              return '%s said, "%s"' % (self.Author, self.news)
-          NameError: Author
-          >>>
-
-        Using Zope's normal debugging methods, you would typically
-        need to start from the "beginning" and step your way down
-        through the debugger to find this error (in this case, the
-        error is pretty obvious, but more often than not errors can be
-        pretty obscure!).
-
-        Post-mortem debugging allows you to jump *directly* to the
-        spot in your code that raised the exception, so you do not
-        need to go through the possibly tedious task of stepping your
-        way through a sea of Python code.  In the case of our example,
-        you can just pass 'ZPublisher.Zope' call a 'pm' argument that
-        is set to 1::
-
-          >>> ZPublisher.Zope('/News/quote', pm=1)
-          Traceback (most recent call last):
-            File "<stdin>", line 1, in ?
-            File "./News.py", line 4, in test
-              test2()
-            File "./News.py", line 3, in test2
-              return '%s said, "%s"' % (self.Author, self.news)
-          NameError: Author
-          (pdb)
-
-        Here, you can see that instead of taking you back to a python
-        prompt, the post mortem debugging flag has caused you to go
-        right into the debugging, *exactly* at the point in your code
-        where the exception is raised.  This can be verified with the
-        debugger's (l)ist command. Post mortem debugging offers you a
-        handy way to jump right to the section of your code that is
-        failing in some obvious way by raising an exception.
-
-      Debugging With ZEO
-
-        ZEO presents some interesting debugging abilities.  ZEO lets
-        you debug one ZEO client when other clients continue to server
-        requests for your site.  In the above examples, you have to
-        shut down Zope to run in the debugger, but with ZEO, you can
-        debug a production site while other clients continue to serve
-        requests. Using ZEO is beyond the scope of this
-        chapter. However, once you have ZEO running, you can debug a
-        client process exactly as you debug a single-process Zope.
-
-          % Anonymous User - Jan. 29, 2004 6:25 am:
-           I there a way to log which objects where
-           written to the ZODB on commit?
-           I want to know the objects (oid would be enough) and the 
-           size of the pickle.
-
-  Unit Testing
-
-    Unit testing allows you to automatically test your classes to make
-    sure they are working correctly. By using unit tests you can make
-    sure as you develop and change your classes that you are not
-    breaking them. Zope comes with Pyunit. You can find out more
-    information on Pyunit at "the Pyunit home
-    page":http://pyunit.sourceforge.net/. Pyunit is also part of the
-    Python "standard
-    library":http://www.python.org/doc/lib/module-unittest.html as of
-    Python 2.1.
-
-      % to_be - May 8, 2003 7:21 am:
-       Some newbie questions:
-       1. Are the tests run automatically when a product is recognised/refreshed?
-       2. If not, is there a possibility to enforce this?
-       3. How to run the tests manually (import problems; PYTHONPATH)?
-
-    What Are Unit Tests
-
-      A "unit" may be defined as a piece of code with a single intended
-      purpose.  A "unit test" is defined as a piece of code which exists
-      to codify the intended behavior of a unit and to compare its
-      intended behavior against its actual behavior.
-
-      Unit tests are a way for developers and quality assurance engineers
-      to quickly ascertain whether independent units of code are working as
-      expected.  Unit tests are generally written at the same time as the
-      code they are intended to test.  A unit testing framework allows a
-      collection of unit tests to be run without human intervention,
-      producing a minimum of output if all the tests in the collection are
-      successful.
-
-      It's a good idea to have a sense of the limits of unit testing.
-      From the "Extreme Programming Enthusiast
-      website":http://c2.com/cgi/wiki?UnitTestsDefined
-      here is a list of things that unit tests are *not*:
-
-        - Manually operated.
-
-        - Automated screen-driver tests that simulate user input (these
-          are "functional tests").
-
-        - Interactive.  They run "no questions asked."
-
-        - Coupled.  They run without dependencies except those native to
-          the thing being tested.
-
-        - Complicated.  Unit test code is typically straightforward
-          procedural code that simulates an event.
-
-    Writing Unit Tests
-
-      Here are the times when
-      you should write unit tests:
-
-        * When you write new code
-
-        * When you change and enhance existing code
-
-        * When you fix bugs
-
-      It's much better to write tests when you're working on code than
-      to wait until you're all done and then write tests.
-
-      You should write tests that exercise discrete "units" of
-      functionality. In other words, write simple, specific tests that test
-      one capability. A good place to start is with interfaces and
-      classes. Classes and especially interfaces already define units of
-      work which you may wish to test.
-
-      Since you can't possibly write tests for every single capability and
-      special case, you should focus on testing the riskiest parts of your
-      code. The riskiest parts are those that would be the most disastrous
-      if they failed. You may also want to test particularly tricky or
-      frequently changed things.
-
-      Here's an example test script that tests the 'News' class defined
-      earlier in this chapter::
-
-        import unittest
-        import News
-
-        class NewsTest(unittest.TestCase):
-
-            def testPost(self):
-                n=News()
-                s='example news'
-                n.postnews(s)
-                assert n.news==s
-
-            def testQuote(self):
-                n=News()
-                s='example news'
-                n.postnews(s)
-                assert n.quote()=='Anonymous said: "%s"' % s
-                a='Author'
-                n.postnews(s, a)
-                assert n.quote()=='%s said: "%s"' % (a, s)
-
-        def test_suite():
-           return unittest.makeSuite(NewsTest, 'news test')
-
-        def main():
-           unittest.TextTestRunner().run(test_suite())
-
-        if __name__=="__main__":
-            main()
-
-      You should save tests inside a 'tests' sub-directory in your
-      product's directory. Test scripts file names should start with
-      test, for example 'testNews.py'. You may accumulate many test
-      scripts in your product's 'tests' directory.  You can run test
-      your product by running the test scripts.
-
-      We cannot cover all there is to say about unit testing here. Take a
-      look at the Pyunit
-      "documentation":http://pyunit.sourceforge.net/pyunit.html for more
-      background on unit testing.
-
-    Zope Test Fixtures
-
-      One issue that you'll run into when unit testing is that you may
-      need to set up a Zope environment in order to test your
-      products. You can solve this problem in two ways. First, you can
-      structure your product so that much of it can be tested without
-      Zope (as you did in the last section). Second, you can create a
-      test fixture that sets up a Zope environment for testing.
-
-      To create a test fixture for Zope you'll
-      need to:
-
-        1. Add Zope's 'lib/python' directory to the Python path.
-
-        2. Import 'Zope' and any other needed Zope modules and packages.
-
-        3. Get a Zope application object.
-
-        4. Do your test using the application object.
-
-        5. Clean up the test by aborting or committing the transaction
-           and closing the Zope database connection.
-
-      Here's an example Zope test fixture that demonstrates how to do
-      each of these steps::
-
-        import os, os.path, sys, string
-        try:
-            import unittest
-        except ImportError:
-            fix_path()
-            import unittest
-
-        class MyTest(unittest.TestCase):
-
-            def setUp(self):
-                # Get the Zope application object and store it in an
-                # instance variable for use by test methods
-                import Zope
-                self.app=Zope.app()
-
-            def tearDown(self):
-                # Abort the transaction and shut down the Zope database
-                # connection.
-                get_transaction().abort()
-                self.app._p_jar.close()
-
-            # At this point your test methods can perform tests using
-            # self.app which refers to the Zope application object.
-
-            ...
-
-        def fix_path():
-            # Add Zope's lib/python directory to the Python path
-            file=os.path.join(os.getcwd(), sys.argv[0])
-            dir=os.path.join('lib', 'python')
-            i=string.find(file, dir)
-            sys.path.insert(0, file[:i+len(dir)])
-
-        def test_suite():
-           return unittest.makeSuite(MyTest, 'my test')
-
-        def main():
-           unittest.TextTestRunner().run(test_suite())
-
-        if __name__=="__main__":
-            fix_path()
-            main()
-
-      This example shows a fairly complete Zope test fixture. If your
-      Zope tests only needs to import Zope modules and packages you can
-      skip getting a Zope application object and closing the database
-      transaction.
-
-      Some times you may run into trouble if your test assuming that
-      there is a current Zope request. There are two ways to deal with
-      this. One is to use the 'makerequest' utility module to create a
-      fake request. For example::
-
-        class MyTest(unittest.TestCase):
-            ...
-
-            def setup(self):
-                import Zope
-                from Testing import makerequest
-                self.app=makerequest.makerequest(Zope.app())
-
-        % Anonymous User - Nov. 9, 2002 3:03 pm:
-         /if your test assuming/if your test assumes/
-
-        % to_be - May 8, 2003 7:13 am:
-         this isn't very understandable for someone who tries to add tests to his product the first time. How is this
-         'app' used then?
-         Is it possible to just create a "REQUEST" object which can then be passed as an argument?
-
-      This will create a Zope application object that is wrapped in a
-      request. This will enable code that expects to acquire a 'REQUEST'
-      attribute work correctly.
-
-      Another solution to testing methods that expect a request is to
-      use the 'ZPublisher.Zope' function described earlier. Using this
-      approach you can simulate HTTP requests in your unit tests. For
-      example::
-
-        import ZPublisher
-
-        class MyTest(unittest.TestCase):
-            ...
-
-            def testWebRequest(self):
-                ZPublisher.Zope('/a/url/representing/a/method?with=a&couple=arguments',
-                                u='username:password', 
-                                s=1, 
-                                e={'some':'environment', 'variable':'settings'})
-
-      If the 's' argument is passed to 'ZPublisher.Zope' then no output
-      will be sent to 'sys.stdout'. If you want to capture the output of
-      the publishing request and compare it to an expected value you'll
-      need to do something like this::
-
-        f=StringIO()
-        temp=sys.stdout
-        sys.stdout=f
-        ZPublisher.Zope('/myobject/mymethod')
-        sys.stdout=temp
-        assert f.getvalue() == expected_output
-
-      Here's a final note on unit testing with a Zope test fixture: you
-      may find Zope helpful. ZEO allows you to test an application while
-      it continues to serve other users. It also speeds Zope start up
-      time which can be a big relief if you start and stop Zope
-      frequently while testing.
-
-      Despite all the attention we've paid to Zope testing fixtures, you
-      should probably concentrate on unit tests that don't require a
-      Zope test fixture. If you can't test much without Zope there is a
-      good chance that your product would benefit from some refactoring
-      to make it simpler and less dependent on the Zope framework.
-
-  Logging
-
-    Zope provides a framework for logging information to Zope's
-    application log. You can configure Zope to write the application log
-    to a file, syslog, or other back-end.
-
-      % Anonymous User - Nov. 16, 2002 4:19 am:
-       Where does logging output go by default? Is that platform-dependent? What's the default logging level?
-
-    The logging API defined in the 'zLOG' module.  This module provides
-    the 'LOG' function which takes the following required arguments:
-
-      subsystem -- The subsystem generating the message (e.g. "ZODB")
-
-      severity -- The "severity" of the event.  This may be an integer
-                  or a floating point number.  Logging back ends may
-                  consider the int() of this value to be significant.
-                  For example, a back-end may consider any severity whose
-                  integer value is WARNING to be a warning.
-
-      summary -- A short summary of the event
-
-    These arguments to the 'LOG' function 
-    are optional:
-
-      detail -- A detailed description
-
-      error -- A three-element tuple consisting of an error type, value, and
-               traceback.  If provided, then a summary of the error is
-               added to the detail.
-
-      reraise -- If provided with a true value, then the error given by
-                 error is reraised.
-
-    You can use the 'LOG' function to send warning and errors to the
-    Zope application log.
-
-    Here's an example of how to use the 'LOG' function to write
-    debugging messages::
-
-      from zLOG import LOG, DEBUG
-      LOG('my app', DEBUG, 'a debugging message')
-
-      % charlesz - Dec. 11, 2002 7:53 am:
-       I get 'import of "LOG" from "zLOG" is unauthorized'. No doubt I'll work out why - but someone else might
-       not.....
-
-    You can use 'LOG' in much the same way as you would use print
-    statements to log debugging information while Zope is running. You
-    should remember that Zope can be configured to ignore log messages
-    below certain levels of severity. If you are not seeing your logging
-    messages, make sure that Zope is configured to write them to the
-    application log.
-
-    In general the debugger is a much more powerful way to locate
-    problems than using the logger. However, for simple debugging tasks
-    and for issuing warnings the logger works just fine.
-
-  Other Testing and Debugging Facilities
-
-    There is a few other testing and debugging techniques and tools
-    not commonly used to test Zope. In this section we'll mention
-    several of them.
-
-      % Anonymous User - Oct. 16, 2001 6:49 pm - The Medusa monitor client is also a good debugging tool. It permits querying the ZODB from an interactive commandline interface, and would be familiar to many Python users. The only downside is that because persisted objects are based on ExtensionClass, it is not possible to query an object for its functions and attributes in the style of "dir(thisobject)".    -- Chui Tey
-
-      % Anonymous User - Nov. 1, 2001 8:39 am - Actually dir(object) works fine.  It just breaks the *first* time you try to use it on a persistent object, where it returns an empty list.  The second and subsequent times, it will show you the methods and attributes.  Of course it won't show you acquired methods and attributes, but that's what aq_parent and such are for. -- chrism
-
-      % Anonymous User - Mar. 6, 2003 5:06 pm:
-       Perhaps it's something I'm doing, but the downside of Medusa monitor debugging on my setup is that all errors
-       fail silently, rather than raising a traceback. So if you try to look at attribute "a" of object "o", and it
-       doesn't exist, you get no response, rather than a TB/exception. Is this just me?
-
-    Debug Logging
-
-      Zope provides an analysis tool for debugging log output.  This
-      output allows may give you hints as to where your application
-      may be performing poorly, or not responding at all.  For
-      example, since writing Zope products lets your write
-      unrestricted Python code, it's very possibly to get yourself in
-      a situation where you "hang" a Zope request, possibly by getting
-      into a infinite loop.
-
-      To try and detect at which point your application hangs, use the
-      *requestprofiler.py* script in the *utilities* directory of your
-      Zope installation.  To use this script, you must run Zope with
-      the '-M' command line option.  This will turn on "detailed debug
-      logging" that is necessary for the *requestprofiler.py* script
-      to run.  The *requestprofiler.py* script has quite a few options
-      which you can learn about with the '--help' switch.
-
-        % chrism - Dec. 10, 2001 11:10 am - Note that the requestprofiler.py script is really just an analysis script for the data collected by in the the detailed debug log.  The description of the relationship above is a little muddled.
-
-      In general debug log analysis should be a last resort. Use it
-      when Zope is hanging and normal debugging and profiling is not
-      helping you solve your problem.
-
-    HTTP Benchmarking
-
-      HTTP load testing is notoriously inaccurate. However, it is
-      useful to have a sense of how many requests your server can
-      support. Zope does not come with any HTTP load testing tools,
-      but there are many available. Apache's 'ab' program is a widely
-      used free tool that can load your server with HTTP requests.
-
-        % chrism - Dec. 10, 2001 11:12 am - HTTP load testing itself is not notoriously inaccurate.  It's probably better to say that it's difficult to do "right".
-
-  Summary
-
-    Zope provides a number of different debugging and testing
-    facilities. The debugger allows you to interactively test your
-    applications. Unit tests allow help you make sure that your
-    application is develops correctly. The logger allows you to do
-    simple debugging and issue warnings.
-
-      % Anonymous User - May 28, 2002 1:57 am:
-       Typo: "Unit tests allow help you ..." should be "Unit tests help you to ..."
-
-      % Anonymous User - Dec. 28, 2003 4:57 am:
-       This doc shows how to check security issues.
-       http://www.zopelabs.com/cookbook/1046449715
-
-    To help maintain your sanity you should keeping your Zope products
-    as simple as possible, use interfaces to describe functionality,
-    and test your components outside as well as inside Zope.
-
-      % Anonymous User - Apr. 8, 2002 9:41 pm:
-       I would nice to have some detail about how to debug security problems. Trying to work out why something is
-       not authorized seems to be the hardest thing I do.
-
-      % Anonymous User - July 2, 2002 11:00 am:
-       I agree with this comment. I am struggling with several permissions problems
-       right now and cannot figure out where to even start looking for clues as 
-       to what might be happening. The callstack traces just don't yield enough
-       information.
-
-      % Anonymous User - Aug. 22, 2005 8:02 am:
-       Every time I read this stupid documentation I end up more annoyed and frustrated, and with my questions still
-       unanswered.

Copied: zdgbook/trunk/source/TestingAndDebugging.rst (from rev 96659, zdgbook/trunk/TestingAndDebugging.stx)
===================================================================
--- zdgbook/trunk/source/TestingAndDebugging.rst	                        (rev 0)
+++ zdgbook/trunk/source/TestingAndDebugging.rst	2009-02-18 05:11:19 UTC (rev 96663)
@@ -0,0 +1,800 @@
+#####################
+Testing and Debugging
+#####################
+
+As you develop Zope applications you will run into problems. This
+chapter covers debugging and testing techniques that can help
+you. The Zope debugger allow you to peek inside a running process and
+find exactly what is going wrong. Unit testing allows you to automate
+the testing process to ensure that your code still works correctly as
+you change it. Finally, Zope provides logging facilities which allow
+you to emit warnings and error messages.
+
+
+Debugging
+=========
+
+Zope provides debugging information through a number of sources. It
+also allows you a couple avenues for getting information about Zope
+as it runs.
+
+The Control Panel
+-----------------
+
+The control panel provides a number of views that can help you debug
+Zope, especially in the area of performance.  The *Debugging
+Information* link on the control panel provides two views, *Debugging
+Info* and *Profiling*.
+
+Debugging info provides information on the number of object
+references and the status of open requests.  The object references
+list displays the name of the object and the number of references to
+that object in Zope.  Understanding how reference counts help
+debugging is a lengthy subject, but in general you can spot memory
+leaks in your application if the number of references to certain
+objects increases without bound.  The busier your site is, or the
+more content it holds, the more reference counts you will tend to
+have.
+
+Profiling uses the standard Python profiler.  This is turned on by
+setting the 'PROFILE_PUBLISHER' environment variable before executing
+Zope.
+
+When the profiler is running, the performance of your Zope system
+will suffer a lot.  Profiling should only be used for short periods
+of time, or on a separate ZEO client so that your normal users to not
+experience this significant penalty.
+
+Profiling provides you with information about which methods in your
+Zope system are taking the most time to execute.  It builds a
+*profile*, which lists the busiest methods on your system, sorted by
+increasing resource usage.  For details on the meaning of the
+profiler's output, read the `standard Python documentation
+<http://www.python.org/doc/current/lib/profile.html>`_
+
+Product Refresh Settings
+------------------------
+
+As of Zope 2.4 there is a *Refresh* view on all Control Panel
+Products. Refresh allows you to reload your product's modules as you
+change them, rather than having to restart Zope to see your
+changes. The *Refresh* view provides the same debugging functionality
+previously provided by Shane Hathaway's Refresh Product.
+
+To turn on product refresh capabilities place a 'refresh.txt' file in
+your product's directory. Then visit the *Refresh* view of your
+product in the management interface. Here you can manually reload
+your product's modules with the *Refresh this product* button. This
+allows you to immediately see the effect of your changes, without
+restarting Zope. You can also turn on automatic refreshing which
+causes Zope to frequently check for changes to your modules and
+refresh your product when it detects that your files have
+changed. Since automatic refresh causes Zope to run more slowly, it
+is a good idea to only turn it on for a few products at a time.
+
+Debug Mode
+----------
+
+Setting the 'Z_DEBUG_MODE=1' environment puts Zope into debug mode.
+This mode reduces the performance of Zope a little bit.  Debug model
+has a number of wide ranging effects:
+
+- Tracebacks are shown on the browser when errors are raised.
+
+- External Methods and DTMLFile objects are checked to see if they
+  have been modified every time they are called.  If modified, they
+  are reloaded.
+
+- Zope will not fork into the background in debug mode, instead, it
+  will remain attached to the terminal that started it and the main
+  logging information will be redirected to that terminal.
+
+Normally, debug mode is set using the '-D' switch when starting Zope,
+though you can set the environment variable directly if you wish.
+
+By using debug mode and product refresh together you will have little
+reason to restart Zope while developing.
+
+The Python Debugger
+-------------------
+
+Zope is integrated with the Python debugger (pdb).  The Python
+debugger is pretty simple as command line debuggers go, and anyone
+familiar with other popular command line debuggers (like gdb) will
+feel right at home in pdb.
+
+For an introduction to pdb see the standard `pdb documentation
+<http://www.python.org/doc/current/lib/module-pdb.html>`_ .
+
+There are a number of ways to debug a Zope process:
+
+  o You can shut down the Zope server and simulate a request on the
+    command line.
+
+  o You can run a special ZEO client that debugs a running server.
+
+  o You can run Zope in debug model and enter the debugger
+    through Zope's terminal session.
+
+The first method is an easy way to debug Zope if you are not running
+ZEO.  First, you must first shut down the Zope process.  It is not
+possible to debug Zope in this way and run it at the same time.
+Starting up the debugger this way will by default start Zope in
+single threaded mode.
+
+For most Zope developer's purposes, the debugger is needed to debug
+some sort of application level programming error.  A common scenario
+is when developing a new product for Zope.  Products extend Zope's
+functionality but they also present the same kind of debugging
+problems that are commonly found in any programming environment.  It
+is useful to have an existing debugging infrastructure to help you
+jump immediately to your new object and debug it and play with it
+directly in pdb.  The Zope debugger lets you do this.
+
+In reality, the "Zope" part of the Zope debugger is actually just a
+handy way to start up Zope with some pre-configured break points and
+to tell the Python debugger where in Zope you want to start
+debugging.
+
+Simulating HTTP Requests
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Now for an example. Remember, for this example to work, you *must*
+shut down Zope. Go to your Zope's 'lib/python' directory and fire up
+Python and import 'Zope' and 'ZPublisher'::
+
+  $ cd lib/python
+  $ python
+  Python 1.5.2 (#0, Apr 13 1999, 10:51:12) [MSC 32 bit (Intel)] on win32
+  Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
+  >>> import Zope, ZPublisher
+  >>>
+
+Here you have run the Python interpreter (which is where using the
+debugger takes place) and imported two modules, 'Zope' and
+'ZPublisher'.  If Python complains about an 'ImportError' and not
+being able to find either module, then you are probably in the wrong
+directory, or you have not compiled Zope properly. If you get this
+message::
+
+  ZODB.POSException.StorageSystemError: Could not lock the
+  database file. There must be another process that has opened
+  the file.
+
+This tells you that Zope is currently running. Shutdown Zope and
+try again.
+
+The 'Zope' module is the main Zope application module. When you
+import 'Zope' it sets up Zope. 'ZPublisher' is the Zope ORB. See
+Chapter 2 for more information about 'ZPublisher'.
+
+You can use the 'ZPublisher.Zope' function to simulate an HTTP
+request. Pass the function a URL relative the your root Zope
+object. Here is an example of how to simulate an HTTP request from
+the debugger::
+
+  >>> ZPublisher.Zope('')
+  Status: 200 OK
+  X-Powered-By: Zope (www.zope.org), Python (www.python.org)
+  Content-Length: 1238
+  Content-Type: text/html
+
+  <HTML><HEAD><TITLE>Zope</TITLE>
+
+    ... blah blah...
+
+  </BODY></HTML>
+  >>> 
+
+If you look closely, you will see that the content returned is
+*exactly* what is returned when you call your root level object
+through HTTP, including all the HTTP headers.
+
+Keep in mind that calling Zope this way does NOT involve a web
+server.  No ports are opened, the 'ZServer' code is not even
+imported.  In fact, this is just an interpreter front end to the same
+application code the ZServer *does* call.
+
+Interactive Debugging
+~~~~~~~~~~~~~~~~~~~~~
+
+Debugging involves publishing a request up to a point where you think
+it's failing, and then inspecting the state of your variables and
+objects.  The easy part is the actual inspection, the hard part is
+getting your program to stop at the right point.
+
+So, for the sake our example, let's say that you have a 'News' object
+which is defined in a Zope Product called 'ZopeNews', and is located
+in the 'lib/python/Products/ZopeNews' directory.  The class that
+defines the 'News' instance is also called 'News', and is defined in
+the 'News.py' module in your product.
+
+Therefore, from Zope's perspective the fully qualified name of your
+class is 'Products.ZopeNews.News.News'.  All Zope objects have this
+kind of fully qualified name.  For example, the 'ZCatalog' class can
+be found in 'Products.ZCatalog.ZCatalog.ZCatalog' (The redundancy is
+because the product, module, and class are all named 'ZCatalog').
+
+Now let's create an example method to debug.  You want your news
+object to have a 'postnews' method, that posts news::
+
+  class News(...):
+
+      ...
+
+      def postnews(self, news, author="Anonymous"):
+          self.news = news
+
+      def quote(self):
+          return '%s said, "%s"' % (self.author, self.news)
+
+You may notice that there's something wrong with the 'postnews'
+method.  The method assigns 'news' to an instance variable, but it
+does nothing with 'author'.  If the 'quote' method is called, it will
+raise an 'AttributeError' when it tries to look up the name
+'self.author'.  Although this is a pretty obvious goof, we'll use it
+to illustrate using the debugger to fix it.
+
+Running the debugger is done in a very similar way to how you called
+Zope through the python interpreter before, except that you introduce
+one new argument to the call to 'Zope'::
+
+  >>> ZPublisher.Zope('/News/postnews?new=blah', d=1)
+  * Type "s<cr>c<cr>" to jump to beginning of real publishing process.
+  * Then type c<cr> to jump to the beginning of the URL traversal
+    algorithm.
+  * Then type c<cr> to jump to published object call.
+  > <string>(0)?()
+  pdb> 
+
+Here, you call Zope from the interpreter, just like before, but there
+are two differences.  First, you call the 'postnews' method with an
+argument using the URL, '/News/postnews?new=blah'.  Second, you
+provided a new argument to the Zope call, 'd=1'.  The 'd' argument,
+when true, causes Zope to fire up in the Python debugger, pdb.
+Notice how the Python prompt changed from '>>>' to 'pdb>'.  This
+indicates that you are in the debugger.
+
+When you first fire up the debugger, Zope gives you a helpful message
+that tells you how to get to your object.  To understand this
+message, it's useful to know how you have set Zope up to be debugged.
+When Zope fires up in debugger mode, there are three breakpoints set
+for you automatically (if you don't know what a breakpoint is, you
+need to read the python `debugger documentation
+<http://www.python.org/doc/current/lib/module-pdb.html>`_).
+
+The first breakpoint stops the program at the point that ZPublisher
+(the Zope ORB) tries to publish the application module (in this case,
+the application module is 'Zope').  The second breakpoint stops the
+program right before ZPublisher tries to traverse down the provided
+URL path (in this case, '/News/postnews').  The third breakpoint will
+stop the program right before ZPublisher calls the object it finds
+that matches the URL path (in this case, the 'News' object).
+
+So, the little blurb that comes up and tells you some keys to press
+is telling you these things in a terse way.  Hitting 's' will *step*
+you into the debugger, and hitting 'c' will *continue* the execution
+of the program until it hits a breakpoint.
+
+Note however that none of these breakpoints will stop the program at
+'postnews'.  To stop the debugger right there, you need to tell the
+debugger to set a new breakpoint.  Why a new breakpoint?  Because
+Zope will stop you before it traverse your objects path, it will stop
+you before it calls the object, but if you want to stop it *exactly*
+at some point in your code, then you have to be explicit.  Sometimes
+the first three breakpoints are convienent, but often you need to set
+your own special break point to get you exactly where you want to go.
+
+Setting a breakpoint is easy (and see the next section for an even
+easier method).  For example::
+
+  pdb> import Products
+  pdb> b Products.ZopeNews.News.News.postnews
+  Breakpoint 5 at C:\Program Files\WebSite\lib\python\Products\ZopeNews\News.py:42
+  pdb> 
+
+First, you import 'Products'.  Since your module is a Zope product,
+it can be found in the 'Products' package.  Next, you set a new
+breakpoint with the *break* debugger command (pdb allows you to use
+single letter commands, but you could have also used the entire word
+'break').  The breakpoint you set is
+'Products.ZopeNews.News.News.postnews'.  After setting this
+breakpoint, the debugger will respond that it found the method in
+question in a certain file, on a certain line (in this case, the
+fictitious line 42) and return you to the debugger.
+
+Now, you want to get to your 'postnews' method so you can start
+debugging it.  But along the way, you must first *continue* through
+the various breakpoints that Zope has set for you.  Although this may
+seem like a bit of a burden, it's actually quite good to get a feel
+for how Zope works internally by getting down the rhythm that Zope
+uses to publish your object.  In these next examples, my comments
+will begin with '#".  Obviously, you won't see these comments when
+you are debugging.  So let's debug::
+
+  pdb> s
+  # 's'tep into the actual debugging
+
+  > <string>(1)?()
+  # this is pdb's response to being stepped into, ignore it
+
+  pdb> c
+  # now, let's 'c'ontinue onto the next breakpoint
+
+  > C:\Program Files\WebSite\lib\python\ZPublisher\Publish.py(112)publish()
+  -> def publish(request, module_name, after_list, debug=0,
+
+  # pdb has stopped at the first breakpoint, which is the point where
+  # ZPubisher tries to publish the application module.
+
+  pdb> c
+  # continuing onto the next breakpoint you get...
+
+  > C:\Program Files\WebSite\lib\python\ZPublisher\Publish.py(101)call_object()
+  -> def call_object(object, args, request):
+
+Here, 'ZPublisher' (which is now publishing the application) has
+found your object and is about to call it.  Calling your object
+consists of applying the arguments supplied by 'ZPublisher' to the
+object.  Here, you can see how 'ZPublisher' is passing three
+arguments into this process.  The first argument is 'object' and is
+the actual object you want to call.  This can be verified by
+*printing* the object::
+
+  pdb> p object
+  <News instance at 00AFE410>
+
+Now you can inspect your object (with the *print* command) and even
+play with it a bit.  The next argument is 'args'.  This is a tuple of
+arguments that 'ZPublisher' will apply to your object call.  The
+final argument is 'request'.  This is the request object and will
+eventually be transformed in to the DTML usable object 'REQUEST'. Now
+continue, your breakpoint is next::
+
+  pdb> c    
+  > C:\Program Files\WebSite\lib\python\Products\ZopeNews\News.py(42)postnews()
+  -> def postnews(self, N)
+
+Now you are here, at your method.  To be sure, tell the debugger to
+show you where you are in the code with the 'l' command. Now you can
+examine variable and perform all the debugging tasks that the Python
+debugger provides.  From here, with a little knowledge of the Python
+debugger, you should be able to do any kind of debugging task that is
+needed.
+
+Interactive Debugging Triggered From the Web
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you are running in debug mode you can set break points in your
+code and then jump straight to the debugger when Zope comes across
+your break points.  Here's how to set a breakpoint::
+
+  import pdb;pdb.set_trace()
+
+Now start Zope in debug mode and point your web browser at a URL that
+causes Zope to execute the method that includes a breakpoint.  When
+this code is executed, the Python debugger will come up in the
+terminal where you started Zope.  Also note that from your web
+browser it looks like Zope is frozen.  Really it's just waiting for
+you do your debugging.
+
+From the terminal you are inside the debugger as it is executing your
+request.  Be aware that you are just debugging one thread in Zope,
+and other requests may be being served by other threads.  If you go
+to the *Debugging Info* screen while in the debugger, you can see
+your debugging request and how long it has been open.
+
+It is often more convenient to use this method to enter the debugger
+than it is to call 'ZPublisher.Zope' as detailed in the last section.
+
+Post-Mortem Debugging
+~~~~~~~~~~~~~~~~~~~~~
+
+Often, you need to use the debugger to chase down obscure problems in
+your code, but sometimes, the problem is obvious, because an
+exception gets raised.  For example, consider the following method on
+your 'News' class::
+
+  def quote(self):
+      return '%s said, "%s"' % (self.Author, self.news)
+
+Here, you can see that the method tries to substitute 'self.Author'
+in a string, but earlier we saw that this should really be
+'self.author'.  If you tried to run this method from the command
+line, an exception would be raised::
+
+  >>> ZPublisher.Zope('/News/quote')
+  Traceback (most recent call last):
+    File "<stdin>", line 1, in ?
+    File "./News.py", line 4, in test
+      test2()
+    File "./News.py", line 3, in test2
+      return '%s said, "%s"' % (self.Author, self.news)
+  NameError: Author
+  >>>
+
+Using Zope's normal debugging methods, you would typically need to
+start from the "beginning" and step your way down through the
+debugger to find this error (in this case, the error is pretty
+obvious, but more often than not errors can be pretty obscure!).
+
+Post-mortem debugging allows you to jump *directly* to the spot in
+your code that raised the exception, so you do not need to go through
+the possibly tedious task of stepping your way through a sea of
+Python code.  In the case of our example, you can just pass
+'ZPublisher.Zope' call a 'pm' argument that is set to 1::
+
+          >>> ZPublisher.Zope('/News/quote', pm=1)
+          Traceback (most recent call last):
+            File "<stdin>", line 1, in ?
+            File "./News.py", line 4, in test
+              test2()
+            File "./News.py", line 3, in test2
+              return '%s said, "%s"' % (self.Author, self.news)
+          NameError: Author
+          (pdb)
+
+Here, you can see that instead of taking you back to a python prompt,
+the post mortem debugging flag has caused you to go right into the
+debugging, *exactly* at the point in your code where the exception is
+raised.  This can be verified with the debugger's (l)ist
+command. Post mortem debugging offers you a handy way to jump right
+to the section of your code that is failing in some obvious way by
+raising an exception.
+
+Debugging With ZEO
+~~~~~~~~~~~~~~~~~~
+
+ZEO presents some interesting debugging abilities.  ZEO lets you
+debug one ZEO client when other clients continue to server requests
+for your site.  In the above examples, you have to shut down Zope to
+run in the debugger, but with ZEO, you can debug a production site
+while other clients continue to serve requests. Using ZEO is beyond
+the scope of this chapter. However, once you have ZEO running, you
+can debug a client process exactly as you debug a single-process
+Zope.
+
+
+Unit Testing
+============
+
+Unit testing allows you to automatically test your classes to make
+sure they are working correctly. By using unit tests you can make
+sure as you develop and change your classes that you are not breaking
+them. Zope comes with Pyunit. You can find out more information on
+Pyunit at `the Pyunit home page <http://pyunit.sourceforge.net/>`_
+. Pyunit is also part of the Python `standard library
+<http://www.python.org/doc/lib/module-unittest.html>`_ as of Python
+2.1.
+
+
+What Are Unit Tests
+-------------------
+
+A "unit" may be defined as a piece of code with a single intended
+purpose.  A "unit test" is defined as a piece of code which exists
+to codify the intended behavior of a unit and to compare its
+intended behavior against its actual behavior.
+
+Unit tests are a way for developers and quality assurance engineers
+to quickly ascertain whether independent units of code are working as
+expected.  Unit tests are generally written at the same time as the
+code they are intended to test.  A unit testing framework allows a
+collection of unit tests to be run without human intervention,
+producing a minimum of output if all the tests in the collection are
+successful.
+
+It's a good idea to have a sense of the limits of unit testing.  From
+the `Extreme Programming Enthusiast website
+<http://c2.com/cgi/wiki?UnitTestsDefined>`_ here is a list of things
+that unit tests are *not*:
+
+- Manually operated.
+
+- Automated screen-driver tests that simulate user input (these are
+  "functional tests").
+
+- Interactive.  They run "no questions asked."
+
+- Coupled.  They run without dependencies except those native to the
+  thing being tested.
+
+- Complicated.  Unit test code is typically straightforward
+  procedural code that simulates an event.
+
+Writing Unit Tests
+------------------
+
+Here are the times when you should write unit tests:
+
+* When you write new code
+
+* When you change and enhance existing code
+
+* When you fix bugs
+
+It's much better to write tests when you're working on code than to
+wait until you're all done and then write tests.
+
+You should write tests that exercise discrete "units" of
+functionality. In other words, write simple, specific tests that test
+one capability. A good place to start is with interfaces and
+classes. Classes and especially interfaces already define units of
+work which you may wish to test.
+
+Since you can't possibly write tests for every single capability and
+special case, you should focus on testing the riskiest parts of your
+code. The riskiest parts are those that would be the most disastrous
+if they failed. You may also want to test particularly tricky or
+frequently changed things.
+
+Here's an example test script that tests the 'News' class defined
+earlier in this chapter::
+
+  import unittest
+  import News
+
+  class NewsTest(unittest.TestCase):
+
+      def testPost(self):
+          n=News()
+          s='example news'
+          n.postnews(s)
+          assert n.news==s
+
+      def testQuote(self):
+          n=News()
+          s='example news'
+          n.postnews(s)
+          assert n.quote()=='Anonymous said: "%s"' % s
+          a='Author'
+          n.postnews(s, a)
+          assert n.quote()=='%s said: "%s"' % (a, s)
+
+  def test_suite():
+     return unittest.makeSuite(NewsTest, 'news test')
+
+  def main():
+     unittest.TextTestRunner().run(test_suite())
+
+  if __name__=="__main__":
+      main()
+
+You should save tests inside a 'tests' sub-directory in your
+product's directory. Test scripts file names should start with test,
+for example 'testNews.py'. You may accumulate many test scripts in
+your product's 'tests' directory.  You can run test your product by
+running the test scripts.
+
+We cannot cover all there is to say about unit testing here. Take a
+look at the Pyunit `documentation
+<http://pyunit.sourceforge.net/pyunit.html>`_ for more background on
+unit testing.
+
+Zope Test Fixtures
+------------------
+
+One issue that you'll run into when unit testing is that you may need
+to set up a Zope environment in order to test your products. You can
+solve this problem in two ways. First, you can structure your product
+so that much of it can be tested without Zope (as you did in the last
+section). Second, you can create a test fixture that sets up a Zope
+environment for testing.
+
+To create a test fixture for Zope you'll need to:
+
+1. Add Zope's 'lib/python' directory to the Python path.
+
+2. Import 'Zope' and any other needed Zope modules and packages.
+
+3. Get a Zope application object.
+
+4. Do your test using the application object.
+
+5. Clean up the test by aborting or committing the transaction and
+   closing the Zope database connection.
+
+Here's an example Zope test fixture that demonstrates how to do each
+of these steps::
+
+  import os, os.path, sys, string
+  try:
+      import unittest
+  except ImportError:
+      fix_path()
+      import unittest
+
+  class MyTest(unittest.TestCase):
+
+      def setUp(self):
+          # Get the Zope application object and store it in an
+          # instance variable for use by test methods
+          import Zope
+          self.app=Zope.app()
+
+      def tearDown(self):
+          # Abort the transaction and shut down the Zope database
+          # connection.
+          get_transaction().abort()
+          self.app._p_jar.close()
+
+      # At this point your test methods can perform tests using
+      # self.app which refers to the Zope application object.
+
+      ...
+
+  def fix_path():
+      # Add Zope's lib/python directory to the Python path
+      file=os.path.join(os.getcwd(), sys.argv[0])
+      dir=os.path.join('lib', 'python')
+      i=string.find(file, dir)
+      sys.path.insert(0, file[:i+len(dir)])
+
+  def test_suite():
+     return unittest.makeSuite(MyTest, 'my test')
+
+  def main():
+     unittest.TextTestRunner().run(test_suite())
+
+  if __name__=="__main__":
+      fix_path()
+      main()
+
+This example shows a fairly complete Zope test fixture. If your Zope
+tests only needs to import Zope modules and packages you can skip
+getting a Zope application object and closing the database
+transaction.
+
+Some times you may run into trouble if your test assuming that there
+is a current Zope request. There are two ways to deal with this. One
+is to use the 'makerequest' utility module to create a fake
+request. For example::
+
+  class MyTest(unittest.TestCase):
+      ...
+
+      def setup(self):
+          import Zope
+          from Testing import makerequest
+          self.app=makerequest.makerequest(Zope.app())
+
+This will create a Zope application object that is wrapped in a
+request. This will enable code that expects to acquire a 'REQUEST'
+attribute work correctly.
+
+Another solution to testing methods that expect a request is to use
+the 'ZPublisher.Zope' function described earlier. Using this approach
+you can simulate HTTP requests in your unit tests. For example::
+
+  import ZPublisher
+
+  class MyTest(unittest.TestCase):
+      ...
+
+      def testWebRequest(self):
+          ZPublisher.Zope('/a/url/representing/a/method?with=a&couple=arguments',
+                          u='username:password', 
+                          s=1, 
+                          e={'some':'environment', 'variable':'settings'})
+
+If the 's' argument is passed to 'ZPublisher.Zope' then no output
+will be sent to 'sys.stdout'. If you want to capture the output of
+the publishing request and compare it to an expected value you'll
+need to do something like this::
+
+  f=StringIO()
+  temp=sys.stdout
+  sys.stdout=f
+  ZPublisher.Zope('/myobject/mymethod')
+  sys.stdout=temp
+  assert f.getvalue() == expected_output
+
+Here's a final note on unit testing with a Zope test fixture: you may
+find Zope helpful. ZEO allows you to test an application while it
+continues to serve other users. It also speeds Zope start up time
+which can be a big relief if you start and stop Zope frequently while
+testing.
+
+Despite all the attention we've paid to Zope testing fixtures, you
+should probably concentrate on unit tests that don't require a Zope
+test fixture. If you can't test much without Zope there is a good
+chance that your product would benefit from some refactoring to make
+it simpler and less dependent on the Zope framework.
+
+Logging
+=======
+
+Zope provides a framework for logging information to Zope's
+application log. You can configure Zope to write the application log
+to a file, syslog, or other back-end.
+
+The logging API defined in the 'zLOG' module.  This module provides
+the 'LOG' function which takes the following required arguments:
+
+- subsystem -- The subsystem generating the message (e.g. "ZODB")
+
+- severity -- The "severity" of the event.  This may be an integer or
+  a floating point number.  Logging back ends may consider the int()
+  of this value to be significant.  For example, a back-end may
+  consider any severity whose integer value is WARNING to be a
+  warning.
+
+- summary -- A short summary of the event
+
+These arguments to the 'LOG' function are optional:
+
+- detail -- A detailed description
+
+- error -- A three-element tuple consisting of an error type, value,
+  and traceback.  If provided, then a summary of the error is added
+  to the detail.
+
+- reraise -- If provided with a true value, then the error given by
+  error is reraised.
+
+You can use the 'LOG' function to send warning and errors to the Zope
+application log.
+
+Here's an example of how to use the 'LOG' function to write debugging
+messages::
+
+  from zLOG import LOG, DEBUG
+  LOG('my app', DEBUG, 'a debugging message')
+
+You can use 'LOG' in much the same way as you would use print
+statements to log debugging information while Zope is running. You
+should remember that Zope can be configured to ignore log messages
+below certain levels of severity. If you are not seeing your logging
+messages, make sure that Zope is configured to write them to the
+application log.
+
+In general the debugger is a much more powerful way to locate
+problems than using the logger. However, for simple debugging tasks
+and for issuing warnings the logger works just fine.
+
+Other Testing and Debugging Facilities
+======================================
+
+There is a few other testing and debugging techniques and tools not
+commonly used to test Zope. In this section we'll mention several of
+them.
+
+Debug Logging
+-------------
+
+Zope provides an analysis tool for debugging log output.  This output
+allows may give you hints as to where your application may be
+performing poorly, or not responding at all.  For example, since
+writing Zope products lets your write unrestricted Python code, it's
+very possibly to get yourself in a situation where you "hang" a Zope
+request, possibly by getting into a infinite loop.
+
+To try and detect at which point your application hangs, use the
+*requestprofiler.py* script in the *utilities* directory of your Zope
+installation.  To use this script, you must run Zope with the '-M'
+command line option.  This will turn on "detailed debug logging" that
+is necessary for the *requestprofiler.py* script to run.  The
+*requestprofiler.py* script has quite a few options which you can
+learn about with the '--help' switch.
+
+In general debug log analysis should be a last resort. Use it when
+Zope is hanging and normal debugging and profiling is not helping you
+solve your problem.
+
+HTTP Benchmarking
+-----------------
+
+HTTP load testing is notoriously inaccurate. However, it is useful to
+have a sense of how many requests your server can support. Zope does
+not come with any HTTP load testing tools, but there are many
+available. Apache's 'ab' program is a widely used free tool that can
+load your server with HTTP requests.
+
+Summary
+=======
+
+Zope provides a number of different debugging and testing
+facilities. The debugger allows you to interactively test your
+applications. Unit tests allow help you make sure that your
+application is develops correctly. The logger allows you to do simple
+debugging and issue warnings.


Property changes on: zdgbook/trunk/source/TestingAndDebugging.rst
___________________________________________________________________
Added: svn:mergeinfo
   + 

Modified: zdgbook/trunk/source/index.rst
===================================================================
--- zdgbook/trunk/source/index.rst	2009-02-18 04:48:42 UTC (rev 96662)
+++ zdgbook/trunk/source/index.rst	2009-02-18 05:11:19 UTC (rev 96663)
@@ -18,6 +18,7 @@
    ZODBPersistentComponents.rst
    Acquisition.rst
    Security.rst
+   TestingAndDebugging.rst
    AppendixA.rst
    AppendixB.rst
 



More information about the Checkins mailing list