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

前端 未结 6 1761
日久生厌
日久生厌 2020-12-02 08:24

Is there a way to automatically start the debugger at the point at which a unittest fails?

Right now I am just using pdb.set_trace() manually, but this is very tedio

相关标签:
6条回答
  • 2020-12-02 08:48

    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.

    try: unittest.findTestCases(__main__).debug()
    except:
        pdb.post_mortem(sys.exc_info()[2])
    

    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()
    
    0 讨论(0)
  • 2020-12-02 09:01

    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.

    0 讨论(0)
  • 2020-12-02 09:02

    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
    
    0 讨论(0)
  • 2020-12-02 09:11

    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
    
    0 讨论(0)
  • 2020-12-02 09:12
    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.

    0 讨论(0)
  • 2020-12-02 09:12

    Third party test framework enhancements generally seem to include the feature (nose and nose2 were already mentioned in other answers). Some more:

    pytest supports it.

    pytest --pdb
    

    Or if you use absl-py's absltest instead of unittest module:

    name_of_test.py --pdb_post_mortem
    
    0 讨论(0)
提交回复
热议问题