TearDown()メソッドでテストの結果(つまり、すべてのアサーションが合格したかどうか)を取得することは可能ですか?私はSeleniumスクリプトを実行しています。tearDown()内からレポートを作成したいのですが、これが可能かどうかわかりません。
警告:現時点では、開発者ボックスから離れて、次の理論を再確認する方法はありません。したがって、これは暗闇でのショットかもしれません。
おそらく、tearDown()メソッド内でsys.exc_info()
の戻り値を確認できます。(None, None, None)
を返す場合、テストケースが成功したことがわかります。それ以外の場合は、返されたTupleを使用して例外オブジェクトに問い合わせることができます。
sys.exc_info ドキュメントを参照してください。
別のより明示的なアプローチは、この特別な処理を必要とするすべてのテストケースメソッドに平手打ちできるメソッドデコレータを記述することです。このデコレータは、アサーション例外をインターセプトでき、それに基づいてself
の状態を変更し、tearDownメソッドが何が起きているかを学習できるようにします。
@assertion_tracker
def test_foo(self):
# some test logic
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 )に変更されました)で機能します。
このソリューションは、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番目の例外が発生する可能性があることを想定しています。したがって、リストerrors
とfailures
には、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ソースリポジトリのコミット履歴に関して):
このソリューションは、他の多くのソリューションと同様にTestCaseインスタンスのprivate属性を使用しますが、関連するすべてを慎重にチェックしましたPythonソースリポジトリで、Python 2.7から3.6.2以降の3つの代替名がギャップなしでコード履歴をカバーすることをコミットします。新しいメジャーPythonリリース後に問題になる可能性がありますが、新しいPythonの場合は、明確に認識され、スキップされ、後で簡単に修正できます。利点は、tearDownを実行する前に何も変更しないことです。テストを中断せず、unittestのすべての機能がサポートされ、pytestで動作し、多くの拡張パッケージで動作しますが、nosetestでは動作しませんunittest.expectedFailure)。
ユーザーテストメソッドのdecoratorsまたはカスタマイズされたfailureException(----(mgilson 、Pavel Repinを使用したソリューション2番目の方法、kenorb)は将来のPythonバージョンに対して堅牢ですが、すべてが完全に機能する場合は、より多くの例外がサポートされ、unittestの内部が複製された雪玉のように成長します。デコレートされた関数は読みにくいトレースバック(1つのデコレータによって追加されるレベルがさらに多くなります)、デバッグがより複雑になり、別のより重要なデコレータに問題がある場合は不快です。 (mgilsonのおかげで、基本的な機能が準備でき、既知の問題を修正できます。)
Modifired run
メソッドとキャッチされたresult
パラメーターを使用したソリューション
result
がtearDown呼び出しの後で更新されるためです。exc_info()
(Pavel Repinの2番目の方法)による解決は、Python 2でのみ機能します。
他のソリューションは原理的には似ていますが、完全ではないか、より多くの欠点があります。
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に設定される理由です。
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:
...
Amatellanesの答えに続いて、Python3.4を使用している場合は、_outcomeForDoCleanups
。一緒にハックすることができたのは次のとおりです。
def _test_has_failed(self):
for method, error in self._outcome.errors:
if error:
return True
return False
不機嫌だが、うまくいくようだ。
どのようなレポートを作成するかによって異なります。
失敗時にアクションを実行したい場合( スクリーンショットの生成 など)、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
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
このデコレータは実際には非常にシンプルです。 unittest
はExceptionsを介して失敗したテストを検出するという事実に依存しています。私の知る限り、処理する必要がある唯一の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
プライベート関数;-)
現在のテストの名前は nittest.TestCase.id() メソッドで取得できます。したがって、tearDownではself.id()を確認できます。
例は次の方法を示しています。
ここでテストした例は、@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)
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
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
オブジェクトに依存する代わりに、代わりに_outcomeForDoCleanups
を others 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時間いじってからハックできる最高の方法です…
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)