Python Unit Testing: Automatically Running the Debugger when a test fails

三世轮回 提交于 2019-11-28 15:59:16
Rosh Oxymoron
import unittest
import sys
import pdb
import functools
import traceback
def debug_on(*exceptions):
    if not exceptions:
        exceptions = (AssertionError, )
    def decorator(f):
        @functools.wraps(f)
        def wrapper(*args, **kwargs):
            try:
                return f(*args, **kwargs)
            except exceptions:
                info = sys.exc_info()
                traceback.print_exception(*info) 
                pdb.post_mortem(info[2])
        return wrapper
    return decorator

class tests(unittest.TestCase):
    @debug_on()
    def test_trigger_pdb(self):
        assert 1 == 0

I corrected the code to call post_mortem on the exception instead of set_trace.

cmcginty

I think what you are looking for is nose. It works like a test runner for unittest.

You can drop into the debugger on errors, with the following command:

nosetests --pdb

A simple option is to just run the tests without result collection and letting the first exception crash down the stack (for arbitrary post mortem handling) by e.g.

unittest.findTestCases(__main__).debug()

Another option: Override unittest.TextTestResult's addError and addFailure in a debug test runner for immediate post_mortem debugging (before tearDown()) - or for collecting and handling errors & tracebacks in an advanced way.

(Doesn't require extra frameworks or an extra decorator for test methods)

Basic example:

import unittest, pdb

class TC(unittest.TestCase):
    def testZeroDiv(self):
        1 / 0

def debugTestRunner(post_mortem=None):
    """unittest runner doing post mortem debugging on failing tests"""
    if post_mortem is None:
        post_mortem = pdb.post_mortem
    class DebugTestResult(unittest.TextTestResult):
        def addError(self, test, err):
            # called before tearDown()
            traceback.print_exception(*err)
            post_mortem(err[2])
            super(DebugTestResult, self).addError(test, err)
        def addFailure(self, test, err):
            traceback.print_exception(*err)
            post_mortem(err[2])
            super(DebugTestResult, self).addFailure(test, err)
    return unittest.TextTestRunner(resultclass=DebugTestResult)

if __name__ == '__main__':
    ##unittest.main()
    unittest.main(testRunner=debugTestRunner())
    ##unittest.main(testRunner=debugTestRunner(pywin.debugger.post_mortem))
    ##unittest.findTestCases(__main__).debug()

Here's a built-in, no extra modules, solution:

import unittest
import sys
import pdb

####################################
def ppdb(e=None):
    """conditional debugging
       use with:  `if ppdb(): pdb.set_trace()` 
    """
    return ppdb.enabled

ppdb.enabled = False
###################################


class SomeTest(unittest.TestCase):

    def test_success(self):
        try:
            pass
        except Exception, e:
            if ppdb(): pdb.set_trace()
            raise

    def test_fail(self):
        try:
            res = 1/0
            #note:  a `nosetests --pdb` run will stop after any exception
            #even one without try/except and ppdb() does not not modify that.
        except Exception, e:
            if ppdb(): pdb.set_trace()
            raise


if __name__ == '__main__':
    #conditional debugging, but not in nosetests
    if "--pdb" in sys.argv:
        print "pdb requested"
        ppdb.enabled = not sys.argv[0].endswith("nosetests")
        sys.argv.remove("--pdb")

    unittest.main()

call it with python myunittest.py --pdb and it will halt. Otherwise it won't.

To apply @cmcginty's answer to the successor nose 2 (recommended by nose available on Debian-based systems via apt-get install nose2), you can drop into the debugger on failures and errors by calling

nose2

in your test directory.

For this, you need to have a suitable .unittest.cfg in your home directory or unittest.cfg in the project directory; it needs to contain the lines

[debugger]
always-on = True
errors-only = False
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!