簡體   English   中英

Python 單元測試:測試失敗時自動運行調試器

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

有沒有辦法在單元測試失敗時自動啟動調試器?

現在我只是手動使用 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()

我想你要找的是鼻子 它就像unittest的測試運行器一樣工作。

您可以使用以下命令在出現錯誤時進入調試器:

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

一個簡單的選擇是只運行測試而不收集結果,並讓第一個異常在堆棧中崩潰(用於任意事后處理),例如

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

第三方測試框架增強功能通常似乎包括該功能(其他答案中已經提到了nosenose2 )。 多一點:

pytest支持它。

pytest --pdb

或者,如果您使用absl-pyabsltest而不是unittest模塊:

name_of_test.py --pdb_post_mortem

要將@cmcginty 的答案應用到后繼鼻子 2基於 Debian 的系統上通過apt-get install nose2提供的鼻子推薦),您可以通過調用進入調試器中的故障和錯誤

nose2

在您的測試目錄中。

為此,您需要在您的主目錄中有一個合適的.unittest.cfg或在項目目錄中有一個unittest.cfg 它需要包含行

[debugger]
always-on = True
errors-only = False

這是一個內置的,沒有額外模塊的解決方案:

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

python myunittest.py --pdb調用它,它會停止。 否則不會。

上面的一些解決方案修改了業務邏輯:

try:      # <-- new code
 original_code()  # <-- changed (indented)
except Exception as e:  # <-- new code
 pdb.post_mortem(...)   # <-- new code

使用裝飾器構建一個模塊,該裝飾器可以對每種類型的錯誤進行驗屍。 裝飾器可以由日志根級別觸發

#!/usr/bin/env python3
'''
Decorator for getting post mortem on errors of a unittest TestCase
'''
import sys
import pdb
import functools
import traceback
import logging
import unittest

logging.basicConfig(format='%(asctime)s %(message)s', level=logging.DEBUG)

def debug_on(log_level):
    '''
    Function decorator for post mortem debugging unittest functions.

    Args:
      log_level (int): logging levels coesponding to logging stl module

    Usecase:

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

    def decorator(f):

        @functools.wraps(f)
        def wrapper(*args, **kwargs):
            try:
                return f(*args, **kwargs)
            except BaseException:
                if log_level < logging.INFO:
                    info = sys.exc_info()
                    traceback.print_exception(*info)
                    pdb.post_mortem(info[2])

        return wrapper

    return decorator


class Debug_onTester(unittest.TestCase):

    @debug_on(logging.root.level)
    def test_trigger_pdb(self):
        assert 1 == 0


if __name__ == '__main__':
    unittest.main()

要解決代碼中的注釋“在文檔中,unittest.TestCase 有一個 debug() 方法,但我不明白如何使用它”,您可以這樣做:

suite = unittest.defaultTestLoader.loadTestsFromModule(sys.modules[__name__])
suite.debug()

單個測試用例的創建方式如下: testCase = tests('test_trigger_pdb') (根據您的示例,其中testsTestCase的子類)。 然后你可以執行testCase.debug()來調試一個案例。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM