File: TestingAndDebugging.stx

package info (click to toggle)
zope-devguide 20011206-2
  • links: PTS
  • area: main
  • in suites: woody
  • size: 456 kB
  • ctags: 45
  • sloc: python: 152; makefile: 124; sh: 54
file content (811 lines) | stat: -rw-r--r-- 34,812 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
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.

  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:

        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.

      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.

    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.