Python单元测试:测试失败时自动启动调试器

56 投票
9 回答
25301 浏览
提问于 2025-04-16 08:17

有没有办法让调试器在单元测试失败的地方自动启动呢?

现在我只是手动使用 pdb.set_trace(),但是这样很麻烦,因为每次都要加上去,最后又要把它去掉。

举个例子:

import unittest

class tests(unittest.TestCase):

    def setUp(self):
        pass

    def test_trigger_pdb(self):
        #this is the way I do it now
        try:
            assert 1==0
        except AssertionError:
            import pdb
            pdb.set_trace()

    def test_no_trigger(self):
        #this is the way I would like to do it:
        a=1
        b=2
        assert a==b
        #magically, pdb would start here
        #so that I could inspect the values of a and b

if __name__=='__main__':
    #In the documentation the unittest.TestCase has a debug() method
    #but I don't understand how to use it
    #A=tests()
    #A.debug(A)

    unittest.main()

9 个回答

4

一个简单的选择是直接运行测试,不收集结果,让第一个出现的错误直接崩溃,这样你可以在之后处理这个错误,比如:

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

另一个选择是,在调试测试运行器中重写 unittest.TextTestResultaddErroraddFailure 方法,这样可以在出错后立即进行调试(在 tearDown() 之前),或者以更高级的方式收集和处理错误及其追踪信息。

(这不需要额外的框架或额外的装饰器来处理测试方法)

基本示例:

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()
37

我觉得你想要的东西是 nose。它的作用就像是一个测试运行器,可以用来配合 unittest 使用。

当出现错误时,你可以通过以下命令进入调试模式:

nosetests --pdb
30
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

我把代码改成了在出现错误时调用 post_mortem,而不是 set_trace。

撰写回答