[英]Python Unit Testing: Automatically Running the Debugger when a test fails
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 tedious as I need to add it each time and take it out at the end.现在我只是手动使用 pdb.set_trace() ,但这非常乏味,因为我需要每次添加它并在最后取出它。
For Example:例如:
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
I corrected the code to call post_mortem on the exception instead of set_trace.我更正了代码以在异常上调用 post_mortem 而不是 set_trace。
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 eg一个简单的选择是只运行测试而不收集结果,并让第一个异常在堆栈中崩溃(用于任意事后处理),例如
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.另一种选择:覆盖unittest.TextTestResult
的addError
和addFailure
在调试测试运行立即验尸调试(前tearDown()
-或用于收集和处理在先进的方式错误和回溯。
(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()
Third party test framework enhancements generally seem to include the feature ( nose
and nose2
were already mentioned in other answers).第三方测试框架增强功能通常似乎包括该功能(其他答案中已经提到了nose
和nose2
)。 Some more:多一点:
pytest supports it. pytest支持它。
pytest --pdb
Or if you use absl-py 's absltest
instead of unittest
module:或者,如果您使用absl-py的absltest
而不是unittest
模块:
name_of_test.py --pdb_post_mortem
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要将@cmcginty 的答案应用到后继鼻子 2 (由基于 Debian 的系统上通过apt-get install nose2
提供的鼻子推荐),您可以通过调用进入调试器中的故障和错误
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;为此,您需要在您的主目录中有一个合适的.unittest.cfg
或在项目目录中有一个unittest.cfg
; it needs to contain the lines它需要包含行
[debugger]
always-on = True
errors-only = False
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.用python myunittest.py --pdb
调用它,它会停止。 Otherwise it won't.否则不会。
Some solution above modifies business logic:上面的一些解决方案修改了业务逻辑:
try: # <-- new code
original_code() # <-- changed (indented)
except Exception as e: # <-- new code
pdb.post_mortem(...) # <-- new code
Buildt a module with a decorator which post mortems into every type of error .使用装饰器构建一个模块,该装饰器可以对每种类型的错误进行验尸。 The decorator can be triggered by the logging root level装饰器可以由日志根级别触发
#!/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()
To address the comment in your code "In the documentation the unittest.TestCase has a debug() method but I don't understand how to use it", you can do something like this:要解决代码中的注释“在文档中,unittest.TestCase 有一个 debug() 方法,但我不明白如何使用它”,您可以这样做:
suite = unittest.defaultTestLoader.loadTestsFromModule(sys.modules[__name__])
suite.debug()
Individual test cases are created like: testCase = tests('test_trigger_pdb')
(where tests
is a sub-class of TestCase
as per your example).单个测试用例的创建方式如下: testCase = tests('test_trigger_pdb')
(根据您的示例,其中tests
是TestCase
的子类)。 And then you can do testCase.debug()
to debug one case.然后你可以执行testCase.debug()
来调试一个案例。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.