50

単体テストが失敗した時点でデバッガーを自動的に起動する方法はありますか?

現在、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()
4

7 に答える 7

36

あなたが探しているのは鼻だと思います。これは、 unittestのテストランナーのように機能します。

次のコマンドを使用して、エラー時にデバッガーにドロップできます。

nosetests --pdb
于 2011-05-16T00:54:29.817 に答える
29
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

set_traceの代わりに例外でpost_mortemを呼び出すようにコードを修正しました。

于 2010-12-09T14:24:54.217 に答える
4

簡単なオプションは、結果を収集せずにテストを実行し、最初の例外を(任意の事後処理のために)スタックにクラッシュさせることです。

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

unittest.TextTestResult別のオプション:オーバーライドaddErroraddFailureデバッグテストランナーで、死後すぐにデバッグするために(前に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()
于 2016-04-18T18:03:45.923 に答える
4

サードパーティのテストフレームワークの拡張機能には、通常、この機能が含まれているようです(他の回答でもすでに言及されています)nosenose2もう少し:

pytestはそれをサポートしています。

pytest --pdb

または、モジュールの代わりにabsl-pyを使用する場合:absltestunittest

name_of_test.py --pdb_post_mortem
于 2019-08-27T22:31:36.713 に答える
1

@cmcgintyの回答を後継のnose2 (Debianベースのシステムで利用可能なnoseによって推奨されています)に適用するには、次のコマンドを使用して、失敗やエラーについてデバッガーにドロップapt-get install nose2できます。

nose2

テストディレクトリにあります。

このために.unittest.cfgは、ホームディレクトリまたはunittest.cfgプロジェクトディレクトリに適切なものが必要です。行を含める必要があります

[debugger]
always-on = True
errors-only = False
于 2016-03-18T12:21:29.320 に答える
0

これが組み込みの、追加のモジュールなしのソリューションです。

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と停止します。そうでなければ、そうではありません。

于 2017-09-05T18:26:07.087 に答える
0

上記のいくつかのソリューションは、ビジネスロジックを変更します。

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

元のコードへの変更を最小限に抑えるために、関数デコレータを定義し、スローする関数を単純にデコレートできます。

def pm(func):
    import functools, pdb

    @functools.wraps(func)
    def func2(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            pdb.post_mortem(e.__traceback__)

   raise
    return func2

使用する:

@pm
def test_xxx(...):
 ...
于 2022-02-04T08:12:59.023 に答える