web-dev-qa-db-ja.com

Pythonのunittestを取得するとtearDown()メソッドが生成されます

TearDown()メソッドでテストの結果(つまり、すべてのアサーションが合格したかどうか)を取得することは可能ですか?私はSeleniumスクリプトを実行しています。tearDown()内からレポートを作成したいのですが、これが可能かどうかわかりません。

61
Joey Robert

警告:現時点では、開発者ボックスから離れて、次の理論を再確認する方法はありません。したがって、これは暗闇でのショットかもしれません。

おそらく、tearDown()メソッド内でsys.exc_info()の戻り値を確認できます。(None, None, None)を返す場合、テストケースが成功したことがわかります。それ以外の場合は、返されたTupleを使用して例外オブジェクトに問い合わせることができます。

sys.exc_info ドキュメントを参照してください。

別のより明示的なアプローチは、この特別な処理を必要とするすべてのテストケースメソッドに平手打ちできるメソッドデコレータを記述することです。このデコレータは、アサーション例外をインターセプトでき、それに基づいてselfの状態を変更し、tearDownメソッドが何が起きているかを学習できるようにします。

@assertion_tracker
def test_foo(self):
    # some test logic
14
Pavel Repin

unittest.TestCase.runの実装を見ると、すべてのテスト結果が引数として渡された結果オブジェクト(通常はunittest.TestResultインスタンス)に収集されていることがわかります。 unittest.TestCaseオブジェクトには結果ステータスが残っていません。

したがって、次のようなテストケースとテスト結果のエレガントな分離を容赦なく壊さない限り、unittest.TestCase.tearDownメソッドでできることはあまりありません。

import unittest

class MyTest(unittest.TestCase):

    currentResult = None # holds last result object passed to run method

    def setUp(self):
        pass

    def tearDown(self):
        ok = self.currentResult.wasSuccessful()
        errors = self.currentResult.errors
        failures = self.currentResult.failures
        print ' All tests passed so far!' if ok else \
                ' %d errors and %d failures so far' % \
                (len(errors), len(failures))

    def run(self, result=None):
        self.currentResult = result # remember result for use in tearDown
        unittest.TestCase.run(self, result) # call superclass run method

    def test_onePlusOneEqualsTwo(self):
        self.assertTrue(1 + 1 == 2) # succeeds

    def test_onePlusOneEqualsThree(self):
        self.assertTrue(1 + 1 == 3) # fails

    def test_onePlusNoneIsNone(self):
        self.assertTrue(1 + None is None) # raises TypeError

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

編集:これはPython 2.6-3.3、(新しいPython bellow )に変更されました)で機能します。

38
scoffey

このソリューションは、Pythonバージョン2.7から3.7(現在の最高バージョン) )、tearDownの前のコードにデコレータやその他の変更を加えない。すべてが、結果の組み込み分類に従って機能します。また、スキップされたテストまたはexpectedFailureは正しく認識されます。現在のテストの結果を評価するもので、これまでに合格したすべてのテストの概要ではありません。 pytest とも互換性があります。

_import unittest

class MyTest(unittest.TestCase):
    def tearDown(self):
        if hasattr(self, '_outcome'):  # Python 3.4+
            result = self.defaultTestResult()  # these 2 methods have no side effects
            self._feedErrorsToResult(result, self._outcome.errors)
        else:  # Python 3.2 - 3.3 or 3.0 - 3.1 and 2.7
            result = getattr(self, '_outcomeForDoCleanups', self._resultForDoCleanups)
        error = self.list2reason(result.errors)
        failure = self.list2reason(result.failures)
        ok = not error and not failure

        # demo:   report short info immediately (not important)
        if not ok:
            typ, text = ('ERROR', error) if error else ('FAIL', failure)
            msg = [x for x in text.split('\n')[1:] if not x.startswith(' ')][0]
            print("\n%s: %s\n     %s" % (typ, self.id(), msg))

    def list2reason(self, exc_list):
        if exc_list and exc_list[-1][0] is self:
            return exc_list[-1][1]

    # DEMO tests
    def test_success(self):
        self.assertEqual(1, 1)

    def test_fail(self):
        self.assertEqual(2, 1)

    def test_error(self):
        self.assertEqual(1 / 0, 1)
_

コメント:tearDownの前にこれ以上は期待できないため、1つまたはゼロの例外(エラーまたは失敗)のみを報告する必要があります。パッケージunittestは、tearDownによって2番目の例外が発生する可能性があることを想定しています。したがって、リストerrorsfailuresには、tearDownの前に1つまたは0個の要素のみを含めることができます。 「デモ」コメントの後の行は、短い結果を報告しています。

デモ出力:(重要ではありません)

_$ python3.5 -m unittest test

EF.
ERROR: test.MyTest.test_error
     ZeroDivisionError: division by zero
FAIL: test.MyTest.test_fail
     AssertionError: 2 != 1

==========================================================
... skipped usual output from unittest with tracebacks ...
...
Ran 3 tests in 0.002s

FAILED (failures=1, errors=1)
_

他のソリューションとの比較Pythonソースリポジトリのコミット履歴に関して):


Pythonソースリポジトリによる説明
=Lib/unittest/case.py=
Python v 2.7-3.3

_class TestCase(object):
    ...
    def run(self, result=None):
        ...
        self._outcomeForDoCleanups = result   # Python 3.2, 3.3
        # self._resultForDoCleanups = result  # Python 2.7
        #                                     # Python 2.6 - no result saved
        ...
        try:
            testMethod()
        except...   # many times for different exception classes
            result.add...(self, sys.exc_info())  # _addSkip, addError, addFailure
        ...
        try:
            self.tearDown()
        ...
_

Python v。3.4-3.6

_    def run(self, result=None):
        ...
        # outocome is a context manager to catch and collect different exceptions
        self._outcome = outcome  
        ...
        with outcome...(self):
            testMethod()
        ...
        with outcome...(self): 
            self.tearDown() 
        ... 
        self._feedErrorsToResult(result, outcome.errors)
_

(Pythonコミットメッセージを読むことにより):Areasonテスト結果がテストからそれほど切り離されている理由は、メモリリークの防止です。すべての例外情報は、すべてのローカル変数を含む、失敗したプロセス状態のフレームにアクセスできます。失敗する可能性のあるコードブロック内のローカル変数にフレームが割り当てられている場合、クロスメモリリファレンスを簡単に作成できます。ガベージコレクターのおかげでひどいものではありませんが、空きメモリは、メモリが正しく解放される場合よりも早く断片化する可能性があります。これが、例外情報とトレースバックが文字列にすぐに変換される理由であり、_self._outcome_のような一時オブジェクトがカプセル化され、メモリリークを防ぐためにfinallyブロックでNoneに設定される理由です。

31
hynekcer

Python2を使用している場合は、_resultForDoCleanupsメソッドを使用できます。このメソッドは TextTestResult オブジェクトを返します:

<unittest.runner.TextTestResult run=1 errors=0 failures=0>

このオブジェクトを使用して、テストの結果を確認できます。

def tearDown(self):
    if self._resultForDoCleanups.failures:
        ...
    Elif self._resultForDoCleanups.errors:
        ...
    else:
        #Success

Python3を使用している場合は、_outcomeForDoCleanupsを使用できます。

def tearDown(self):
    if not self._outcomeForDoCleanups.success:
        ...
10
amatellanes

Amatellanesの答えに続いて、Python3.4を使用している場合は、_outcomeForDoCleanups。一緒にハックすることができたのは次のとおりです。

def _test_has_failed(self):
    for method, error in self._outcome.errors:
        if error:
            return True
    return False

不機嫌だが、うまくいくようだ。

9
hwjp

どのようなレポートを作成するかによって異なります。

失敗時にアクションを実行したい場合( スクリーンショットの生成 など)、tearDown()を使用する代わりに、failureExceptionをオーバーライドすることでそれを実現できます。 。

例えば:

@property
def failureException(self):
    class MyFailureException(AssertionError):
        def __init__(self_, *args, **kwargs):
            screenshot_dir = 'reports/screenshots'
            if not os.path.exists(screenshot_dir):
                os.makedirs(screenshot_dir)
            self.driver.save_screenshot('{0}/{1}.png'.format(screenshot_dir, self.id()))
            return super(MyFailureException, self_).__init__(*args, **kwargs)
    MyFailureException.__= AssertionError.__name__
    return MyFailureException
8
kenorb

unittest内部に依存するソリューションを使用して不快な私たちのためのソリューションは次のとおりです。

最初に、TestCaseインスタンスにフラグを設定して、テストケースが失敗または合格したかどうかを判断するデコレータを作成します。

import unittest
import functools

def _tag_error(func):
    """Decorates a unittest test function to add failure information to the TestCase."""

    @functools.wraps(func)
    def decorator(self, *args, **kwargs):
        """Add failure information to `self` when `func` raises an exception."""
        self.test_failed = False
        try:
            func(self, *args, **kwargs)
        except unittest.SkipTest:
            raise
        except Exception:  # pylint: disable=broad-except
            self.test_failed = True
            raise  # re-raise the error with the original traceback.

    return decorator

このデコレータは実際には非常にシンプルです。 unittestExceptionsを介して失敗したテストを検出するという事実に依存しています。私の知る限り、処理する必要がある唯一のspecial例外はunittest.SkipTest(テストの失敗を示すものではありません)。他のすべての例外はテストの失敗を示しているため、それらがバブルアップしたときにそのようなものとしてマークします。

これで、このデコレータを直接使用できます。

class MyTest(unittest.TestCase):
    test_failed = False

    def tearDown(self):
        super(MyTest, self).tearDown()
        print(self.test_failed)

    @_tag_error
    def test_something(self):
        self.fail('Bummer')

このデコレータを常に書くのは本当に面倒です。単純化できる方法はありますか?はいあります!* デコレータの適用を処理するメタクラスを作成できます。

class _TestFailedMeta(type):
    """Metaclass to decorate test methods to append error information to the TestCase instance."""
    def __new__(cls, name, bases, dct):
        for name, prop in dct.items():
            # assume that TestLoader.testMethodPrefix hasn't been messed with -- otherwise, we're hosed.
            if name.startswith('test') and callable(prop):
                dct[name] = _tag_error(prop)

        return super(_TestFailedMeta, cls).__new__(cls, name, bases, dct)

これでベースTestCaseサブクラスにこれを適用し、すべて設定できました。

import six  # For python2.x/3.x compatibility

class BaseTestCase(six.with_metaclass(_TestFailedMeta, unittest.TestCase)):
    """Base class for all our other tests.

    We don't really need this, but it demonstrates that the
    metaclass gets applied to all subclasses too.
    """


class MyTest(BaseTestCase):

    def tearDown(self):
        super(MyTest, self).tearDown()
        print(self.test_failed)

    def test_something(self):
        self.fail('Bummer')

多くの場合、これが適切に処理されません。たとえば、は、失敗した subtestsまたは予想される失敗を正しく検出しません。この他の障害モードに興味があるので、適切に処理していないケースを見つけた場合は、コメントでお知らせください。調査します。


*もっと簡単な方法がなかったら、私は_tag_errorプライベート関数;-)

3
mgilson

現在のテストの名前は nittest.TestCase.id() メソッドで取得できます。したがって、tearDownではself.id()を確認できます。

例は次の方法を示しています。

  • 現在のテストでエラーまたはエラーのリストにエラーまたはエラーがあるかどうかを調べる
  • テストIDをPASSまたはFAILまたはEXCEPTIONで出力します

ここでテストした例は、@scoffeyのNiceの例で機能します。

def tearDown(self):
    result = "PASS"
    #### find and show result for current test
    # I did not find any nicer/neater way of comparing self.id() with test id stored in errors or failures lists :-7
    id = str(self.id()).split('.')[-1]
    # id() e.g. tup[0]:<__main__.MyTest testMethod=test_onePlusNoneIsNone>
    #           str(tup[0]):"test_onePlusOneEqualsThree (__main__.MyTest)"
    #           str(self.id()) = __main__.MyTest.test_onePlusNoneIsNone
    for tup in self.currentResult.failures:
        if str(tup[0]).startswith(id):
            print ' test %s failure:%s' % (self.id(), tup[1])
            ## DO TEST FAIL ACTION HERE
            result = "FAIL"
    for tup in self.currentResult.errors:
        if str(tup[0]).startswith(id):
            print ' test %s error:%s' % (self.id(), tup[1])
            ## DO TEST EXCEPTION ACTION HERE
            result = "EXCEPTION"

    print "Test:%s Result:%s" % (self.id(), result)

結果の例:

python run_scripts/tut2.py 2>&1 
E test __main__.MyTest.test_onePlusNoneIsNone error:Traceback (most recent call last):
  File "run_scripts/tut2.py", line 80, in test_onePlusNoneIsNone
    self.assertTrue(1 + None is None) # raises TypeError
TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'

Test:__main__.MyTest.test_onePlusNoneIsNone Result:EXCEPTION
F test __main__.MyTest.test_onePlusOneEqualsThree failure:Traceback (most recent call last):
  File "run_scripts/tut2.py", line 77, in test_onePlusOneEqualsThree
    self.assertTrue(1 + 1 == 3) # fails
AssertionError: False is not true

Test:__main__.MyTest.test_onePlusOneEqualsThree Result:FAIL
Test:__main__.MyTest.test_onePlusOneEqualsTwo Result:PASS
.
======================================================================
ERROR: test_onePlusNoneIsNone (__main__.MyTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "run_scripts/tut2.py", line 80, in test_onePlusNoneIsNone
    self.assertTrue(1 + None is None) # raises TypeError
TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'

======================================================================
FAIL: test_onePlusOneEqualsThree (__main__.MyTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "run_scripts/tut2.py", line 77, in test_onePlusOneEqualsThree
     self.assertTrue(1 + 1 == 3) # fails
AssertionError: False is not true

----------------------------------------------------------------------
Ran 3 tests in 0.001s

FAILED (failures=1, errors=1)
1
gaoithe

Python 2.7。

Unittest.main()の後に結果を取得することもできます:

t = unittest.main(exit=False)
print t.result

またはスイートを使用:

suite.addTests(tests)
result = unittest.result.TestResult()
suite.run(result)
print result
1
junfx

scoffey's answer に触発されて、 mercilessnes を次のレベルに進めることにし、次のことを思いつきました。

Vanillaの単体テストと、nosetestを介して実行した場合の両方で機能し、Pythonバージョン2.7、3.2、3.3、3.4(特に3.0、3.1、3.5をテストしませんでしたが、現時点ではこれらをインストールしていませんが、 ソースコード を正しく読んだ場合、3.5でも動作するはずです):

#! /usr/bin/env python

from __future__ import unicode_literals
import logging
import os
import sys
import unittest


# Log file to see squawks during testing
formatter = logging.Formatter(fmt='%(levelname)-8s %(name)s: %(message)s')
log_file = os.path.splitext(os.path.abspath(__file__))[0] + '.log'
handler = logging.FileHandler(log_file)
handler.setFormatter(formatter)
logging.root.addHandler(handler)
logging.root.setLevel(logging.DEBUG)
log = logging.getLogger(__name__)


PY = Tuple(sys.version_info)[:3]


class SmartTestCase(unittest.TestCase):

    """Knows its state (pass/fail/error) by the time its tearDown is called."""

    def run(self, result):
        # Store the result on the class so tearDown can behave appropriately
        self.result = result.result if hasattr(result, 'result') else result
        if PY >= (3, 4, 0):
            self._feedErrorsToResultEarly = self._feedErrorsToResult
            self._feedErrorsToResult = lambda *args, **kwargs: None  # no-op
        super(SmartTestCase, self).run(result)

    @property
    def errored(self):
        if (3, 0, 0) <= PY < (3, 4, 0):
            return bool(self._outcomeForDoCleanups.errors)
        return self.id() in [case.id() for case, _ in self.result.errors]

    @property
    def failed(self):
        if (3, 0, 0) <= PY < (3, 4, 0):
            return bool(self._outcomeForDoCleanups.failures)
        return self.id() in [case.id() for case, _ in self.result.failures]

    @property
    def passed(self):
        return not (self.errored or self.failed)

    def tearDown(self):
        if PY >= (3, 4, 0):
            self._feedErrorsToResultEarly(self.result, self._outcome.errors)


class TestClass(SmartTestCase):

    def test_1(self):
        self.assertTrue(True)

    def test_2(self):
        self.assertFalse(True)

    def test_3(self):
        self.assertFalse(False)

    def test_4(self):
        self.assertTrue(False)

    def test_5(self):
        self.assertHerp('Derp')

    def tearDown(self):
        super(TestClass, self).tearDown()
        log.critical('---- RUNNING {} ... -----'.format(self.id()))
        if self.errored:
            log.critical('----- ERRORED -----')
        Elif self.failed:
            log.critical('----- FAILED -----')
        else:
            log.critical('----- PASSED -----')


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

unittestで実行する場合:

$ ./test.py -v
test_1 (__main__.TestClass) ... ok
test_2 (__main__.TestClass) ... FAIL
test_3 (__main__.TestClass) ... ok
test_4 (__main__.TestClass) ... FAIL
test_5 (__main__.TestClass) ... ERROR
[…]

$ cat ./test.log
CRITICAL __main__: ---- RUNNING __main__.TestClass.test_1 ... -----
CRITICAL __main__: ----- PASSED -----
CRITICAL __main__: ---- RUNNING __main__.TestClass.test_2 ... -----
CRITICAL __main__: ----- FAILED -----
CRITICAL __main__: ---- RUNNING __main__.TestClass.test_3 ... -----
CRITICAL __main__: ----- PASSED -----
CRITICAL __main__: ---- RUNNING __main__.TestClass.test_4 ... -----
CRITICAL __main__: ----- FAILED -----
CRITICAL __main__: ---- RUNNING __main__.TestClass.test_5 ... -----
CRITICAL __main__: ----- ERRORED -----

nosetestsで実行する場合:

$ nosetests ./test.py -v
test_1 (test.TestClass) ... ok
test_2 (test.TestClass) ... FAIL
test_3 (test.TestClass) ... ok
test_4 (test.TestClass) ... FAIL
test_5 (test.TestClass) ... ERROR

$ cat ./test.log
CRITICAL test: ---- RUNNING test.TestClass.test_1 ... -----
CRITICAL test: ----- PASSED -----
CRITICAL test: ---- RUNNING test.TestClass.test_2 ... -----
CRITICAL test: ----- FAILED -----
CRITICAL test: ---- RUNNING test.TestClass.test_3 ... -----
CRITICAL test: ----- PASSED -----
CRITICAL test: ---- RUNNING test.TestClass.test_4 ... -----
CRITICAL test: ----- FAILED -----
CRITICAL test: ---- RUNNING test.TestClass.test_5 ... -----
CRITICAL test: ----- ERRORED -----

バックグラウンド

I started これで:

class SmartTestCase(unittest.TestCase):

    """Knows its state (pass/fail/error) by the time its tearDown is called."""

    def run(self, result):
        # Store the result on the class so tearDown can behave appropriately
        self.result = result.result if hasattr(result, 'result') else result
        super(SmartTestCase, self).run(result)

    @property
    def errored(self):
        return self.id() in [case.id() for case, _ in self.result.errors]

    @property
    def failed(self):
        return self.id() in [case.id() for case, _ in self.result.failures]

    @property
    def passed(self):
        return not (self.errored or self.failed)

ただし、これはPython 2.でのみ機能します。Python 3、3.3までは、制御フローが少し変更されたように見えます:Python 3のunittestパッケージ プロセス結果 after 各テストのtearDown()メソッドを呼び出しています...この動作は、テストクラスに追加の行(または6)を追加するだけです。

@@ -63,6 +63,12 @@
             log.critical('----- FAILED -----')
         else:
             log.critical('----- PASSED -----')
+        log.warning(
+            'ERRORS THUS FAR:\n'
+            + '\n'.join(tc.id() for tc, _ in self.result.errors))
+        log.warning(
+            'FAILURES THUS FAR:\n'
+            + '\n'.join(tc.id() for tc, _ in self.result.failures))


 if __== '__main__':

次に、テストを再実行します。

$ python3.3 ./test.py -v
test_1 (__main__.TestClass) ... ok
test_2 (__main__.TestClass) ... FAIL
test_3 (__main__.TestClass) ... ok
test_4 (__main__.TestClass) ... FAIL
test_5 (__main__.TestClass) ... ERROR
[…]

…そして結果としてこれを得ることがわかります:

CRITICAL __main__: ---- RUNNING __main__.TestClass.test_1 ... -----
CRITICAL __main__: ----- PASSED -----
WARNING  __main__: ERRORS THUS FAR:

WARNING  __main__: FAILURES THUS FAR:

CRITICAL __main__: ---- RUNNING __main__.TestClass.test_2 ... -----
CRITICAL __main__: ----- PASSED -----
WARNING  __main__: ERRORS THUS FAR:

WARNING  __main__: FAILURES THUS FAR:

CRITICAL __main__: ---- RUNNING __main__.TestClass.test_3 ... -----
CRITICAL __main__: ----- PASSED -----
WARNING  __main__: ERRORS THUS FAR:

WARNING  __main__: FAILURES THUS FAR:
__main__.TestClass.test_2
CRITICAL __main__: ---- RUNNING __main__.TestClass.test_4 ... -----
CRITICAL __main__: ----- PASSED -----
WARNING  __main__: ERRORS THUS FAR:

WARNING  __main__: FAILURES THUS FAR:
__main__.TestClass.test_2
CRITICAL __main__: ---- RUNNING __main__.TestClass.test_5 ... -----
CRITICAL __main__: ----- PASSED -----
WARNING  __main__: ERRORS THUS FAR:

WARNING  __main__: FAILURES THUS FAR:
__main__.TestClass.test_2
__main__.TestClass.test_4

さて、上記をPython 2の出力と比較してください:

CRITICAL __main__: ---- RUNNING __main__.TestClass.test_1 ... -----
CRITICAL __main__: ----- PASSED -----
WARNING  __main__: ERRORS THUS FAR:

WARNING  __main__: FAILURES THUS FAR:

CRITICAL __main__: ---- RUNNING __main__.TestClass.test_2 ... -----
CRITICAL __main__: ----- FAILED -----
WARNING  __main__: ERRORS THUS FAR:

WARNING  __main__: FAILURES THUS FAR:
__main__.TestClass.test_2
CRITICAL __main__: ---- RUNNING __main__.TestClass.test_3 ... -----
CRITICAL __main__: ----- PASSED -----
WARNING  __main__: ERRORS THUS FAR:

WARNING  __main__: FAILURES THUS FAR:
__main__.TestClass.test_2
CRITICAL __main__: ---- RUNNING __main__.TestClass.test_4 ... -----
CRITICAL __main__: ----- FAILED -----
WARNING  __main__: ERRORS THUS FAR:

WARNING  __main__: FAILURES THUS FAR:
__main__.TestClass.test_2
__main__.TestClass.test_4
CRITICAL __main__: ---- RUNNING __main__.TestClass.test_5 ... -----
CRITICAL __main__: ----- ERRORED -----
WARNING  __main__: ERRORS THUS FAR:
__main__.TestClass.test_5
WARNING  __main__: FAILURES THUS FAR:
__main__.TestClass.test_2
__main__.TestClass.test_4

Python 3プロセスエラー/失敗 after テストが破棄されたので、result.errorsまたはresult.failures in (おそらく、テスト結果を after 分解して処理する方がアーキテクチャ的には理にかなっていると思いますが、 does テストの合格/不合格の状況に応じて、テストが終了するまでの手順が完全に有効なユースケース。

したがって、全体のresultオブジェクトに依存する代わりに、代わりに_outcomeForDoCleanupsothers have already として参照できます。これには、現在実行中の結果オブジェクトが含まれますテストし、必要なerrorsおよびfailrues属性を使用して、tearDown()が呼び出されるまでにテストのステータスを推測できます。

@@ -3,6 +3,7 @@
 from __future__ import unicode_literals
 import logging
 import os
+import sys
 import unittest


@@ -16,6 +17,9 @@
 log = logging.getLogger(__name__)


+PY = Tuple(sys.version_info)[:3]
+
+
 class SmartTestCase(unittest.TestCase):

     """Knows its state (pass/fail/error) by the time its tearDown is called."""
@@ -27,10 +31,14 @@

     @property
     def errored(self):
+        if PY >= (3, 0, 0):
+            return bool(self._outcomeForDoCleanups.errors)
         return self.id() in [case.id() for case, _ in self.result.errors]

     @property
     def failed(self):
+        if PY >= (3, 0, 0):
+            return bool(self._outcomeForDoCleanups.failures)
         return self.id() in [case.id() for case, _ in self.result.failures]

     @property

これにより、Python 3.の初期バージョンのサポートが追加されます。

Python 3.4、ただし、このプライベートメンバー変数 もはや存在しない 、代わりに、新しい(ただし also private)メソッドが追加されました: _feedErrorsToResult

これは、バージョン3.4( およびそれ以降 )で、ニーズが十分に大きい場合、 very hackishly force バージョン2で行ったようにすべてを再び機能させる方法

@@ -27,17 +27,20 @@
     def run(self, result):
         # Store the result on the class so tearDown can behave appropriately
         self.result = result.result if hasattr(result, 'result') else result
+        if PY >= (3, 4, 0):
+            self._feedErrorsToResultEarly = self._feedErrorsToResult
+            self._feedErrorsToResult = lambda *args, **kwargs: None  # no-op
         super(SmartTestCase, self).run(result)

     @property
     def errored(self):
-        if PY >= (3, 0, 0):
+        if (3, 0, 0) <= PY < (3, 4, 0):
             return bool(self._outcomeForDoCleanups.errors)
         return self.id() in [case.id() for case, _ in self.result.errors]

     @property
     def failed(self):
-        if PY >= (3, 0, 0):
+        if (3, 0, 0) <= PY < (3, 4, 0):
             return bool(self._outcomeForDoCleanups.failures)
         return self.id() in [case.id() for case, _ in self.result.failures]

@@ -45,6 +48,10 @@
     def passed(self):
         return not (self.errored or self.failed)

+    def tearDown(self):
+        if PY >= (3, 4, 0):
+            self._feedErrorsToResultEarly(self.result, self._outcome.errors)
+

 class TestClass(SmartTestCase):

@@ -64,6 +71,7 @@
         self.assertHerp('Derp')

     def tearDown(self):
+        super(TestClass, self).tearDown()
         log.critical('---- RUNNING {} ... -----'.format(self.id()))
         if self.errored:
             log.critical('----- ERRORED -----')

…提供、もちろん、このクラスのすべてのコンシューマーは、それぞれのtearDownメソッドでsuper(…, self).tearDown()を覚えています…

免責事項:純粋に教育的で、自宅などでこれを試してはいけませんなど。 、そして土曜日の午後に1〜2時間いじってからハックできる最高の方法です…

1
Mark G.

python 3.7-失敗したアサーションの情報を取得するためのサンプルコードですが、エラーに対処する方法を知ることができます。

def tearDown(self):
    if self._outcome.errors[1][1] and hasattr(self._outcome.errors[1][1][1], 'actual'):
        print(self._testMethodName)
        print(self._outcome.errors[1][1][1].actual)
        print(self._outcome.errors[1][1][1].expected)
0
pbaranski