[英]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()
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.TextTestResult
的addError
和addFailure
在調試測試運行立即驗屍調試(前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()
要將@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')
(根據您的示例,其中tests
是TestCase
的子類)。 然后你可以執行testCase.debug()
來調試一個案例。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.