Text File
testrunner-debugging.txt

Debugging

The testrunner module supports post-mortem debugging and debugging using pdb.set_trace. Let's look first at using pdb.set_trace. To demonstrate this, we'll provide input via helper Input objects:

>>> class Input:
...     def __init__(self, src):
...         self.lines = src.split('\n')
...     def readline(self):
...         line = self.lines.pop(0)
...         print line
...         return line+'\n'

If a test or code called by a test calls pdb.set_trace, then the runner will enter pdb at that point:

>>> import os.path, sys
>>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex')
>>> from zope.testing import testrunner
>>> defaults = [
...     '--path', directory_with_tests,
...     '--tests-pattern', '^sampletestsf?$',
...     ]
>>> real_stdin = sys.stdin
>>> if sys.version_info[:2] == (2, 3):
...     sys.stdin = Input('n\np x\nc')
... else:
...     sys.stdin = Input('p x\nc')
>>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$'
...             ' -t set_trace1').split()
>>> try: testrunner.run(defaults)
... finally: sys.stdin = real_stdin
... # doctest: +ELLIPSIS
Running unit tests:...
> testrunner-ex/sample3/sampletests_d.py(27)test_set_trace1()
-> y = x
(Pdb) p x
1
(Pdb) c
  Ran 1 tests with 0 failures and 0 errors in 0.001 seconds.
False

Note that, prior to Python 2.4, calling pdb.set_trace caused pdb to break in the pdb.set_trace function. It was necessary to use 'next' or 'up' to get to the application code that called pdb.set_trace. In Python 2.4, pdb.set_trace causes pdb to stop right after the call to pdb.set_trace.

You can also do post-mortem debugging, using the --post-mortem (-D) option:

>>> sys.stdin = Input('p x\nc')
>>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$'
...             ' -t post_mortem1 -D').split()
>>> try: testrunner.run(defaults)
... finally: sys.stdin = real_stdin
... # doctest: +NORMALIZE_WHITESPACE +REPORT_NDIFF
Running unit tests:
<BLANKLINE>
<BLANKLINE>
Error in test test_post_mortem1 (sample3.sampletests_d.TestSomething)
Traceback (most recent call last):
  File "testrunner-ex/sample3/sampletests_d.py",
          line 34, in test_post_mortem1
    raise ValueError
ValueError
<BLANKLINE>
exceptions.ValueError:
<BLANKLINE>
> testrunner-ex/sample3/sampletests_d.py(34)test_post_mortem1()
-> raise ValueError
(Pdb) p x
1
(Pdb) c
True

Note that the test runner exits after post-mortem debugging.

In the example above, we debugged an error. Failures are actually converted to errors and can be debugged the same way:

>>> sys.stdin = Input('up\np x\np y\nc')
>>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$'
...             ' -t post_mortem_failure1 -D').split()
>>> try: testrunner.run(defaults)
... finally: sys.stdin = real_stdin
... # doctest: +NORMALIZE_WHITESPACE +REPORT_NDIFF
Running unit tests:
<BLANKLINE>
<BLANKLINE>
Error in test test_post_mortem_failure1 (sample3.sampletests_d.TestSomething)
Traceback (most recent call last):
  File ".../unittest.py",  line 252, in debug
    getattr(self, self.__testMethodName)()
  File "testrunner-ex/sample3/sampletests_d.py",
    line 42, in test_post_mortem_failure1
    self.assertEqual(x, y)
  File ".../unittest.py", line 302, in failUnlessEqual
    raise self.failureException, \
AssertionError: 1 != 2
<BLANKLINE>
exceptions.AssertionError:
1 != 2
> .../unittest.py(302)failUnlessEqual()
-> ...
(Pdb) up
> testrunner-ex/sample3/sampletests_d.py(42)test_post_mortem_failure1()
-> self.assertEqual(x, y)
(Pdb) p x
1
(Pdb) p y
2
(Pdb) c
True