You are not logged in Log in Join
You are here: Home » Zope Documentation » Books » The Zope Developer's Guide Releases » Zope Developer's Guide (2.4 edition) » Testing and Debugging

Log in
Name

Password

 
Previous Page Up one Level Next Page Testing and Debugging Comments On/Off Table of Contents

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.

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:

  • 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.

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.

There are a number of ways to debug a Zope process:

  • You can shut down the Zope server and simulate a request on the command line.
  • You can run a special ZEO client that debugs a running server.
  • 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.).

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. Pyunit is also part of the Python standard library 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

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 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.

Previous Page Up one Level Next Page Testing and Debugging Comments On/Off Table of Contents