Python의 unittest를 가져 오면 tearDown () 메서드가 생성됩니다.
tearDown () 메서드에서 테스트 결과 (즉, 모든 주장이 통과되었는지 여부)를 얻을 수 있습니까? Selenium 스크립트를 실행 중이며 tearDown () 내부에서보고를하고 싶지만 이것이 가능한지 모르겠습니다.
주의 : 현재 다음 이론을 재확인 할 방법이 없습니다. 그래서 이것은 어둠 속에서 촬영할 수 있습니다.
sys.exc_info()
tearDown () 메서드 내부 의 반환 값을 확인할 수 있습니다.을 반환 (None, None, None)
하면 테스트 케이스가 성공한 것입니다. 그렇지 않으면 반환 된 튜플을 사용하여 예외 개체를 조사 할 수 있습니다.
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 __name__ == '__main__':
unittest.main()
편집 : 이것은 Python 2.6-3.3에서 작동합니다 (새 Python 벨로우즈에 맞게 수정 됨 ).
이 솔루션은 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)
설명 : 이전에는 더 이상을 예상 할 수 없으므로 예외 (오류 또는 실패)를 하나 또는 0 개만보고하면됩니다 tearDown
. 패키지 unittest
는 tearDown에 의해 두 번째 예외가 발생할 수 있다고 예상합니다. 따라서 목록 errors
과 failures
는 tearDown 전에 하나 또는 0 개의 요소 만 포함 할 수 있습니다. "demo"주석 뒤의 줄은 짧은 결과를보고합니다.
데모 출력 : (중요하지 않음)
$ 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 인스턴스 의 개인 속성 을 사용 하지만 Python 소스 저장소의 모든 관련 커밋을 신중하게 확인하여 Python 2.7부터 3.6.2까지의 코드 기록을 공백없이 세 개의 대체 이름이 포함하는지 확인했습니다. 일부 새로운 주요 Python 릴리스 이후에 문제가 될 수 있지만 명확하게 인식되고 건너 뛰고 나중에 새로운 Python에 대해 쉽게 고칠 수 있습니다. 장점은 tearDown을 실행하기 전에 아무것도 수정되지 않고 테스트를 중단해서는 안되며 unittest의 모든 기능이 지원되고 pytest와 함께 작동하며 많은 확장 패키지에서 작동 할 수 있지만 nosetest와는 작동하지 않을 수 있다는 것입니다 (예 : nosetest는 호환되지 않습니다. unittest.expectedFailure 포함).
가진 솔루션을 장식 사용자 시험 방법 또는 사용자 정의와 failureException ( mgilson , 파벨 출처 두번째 방법, kenorb)는 미래의 파이썬 버전에 대한 강력하지만, 모든 것이 완벽하게 작동해야하는 경우, 그들은 더 많은 지원 예외와 눈 공처럼 성장할 것 unittest의 더 많은 복제 내부. 데코 레이팅 된 함수는 가독성이 낮은 트레이스 백 (한 데코레이터에 의해 더 많은 레벨이 추가됨)을 가지고 있으며 디버깅에 더 복잡하며 다른 더 중요한 데코레이터에 문제가있는 경우 불쾌합니다. (mgilson 덕분에 기본 기능이 준비되었으며 알려진 문제를 수정할 수 있습니다.)
수정 된
run
메서드 와 포착 된result
매개 변수가 있는 솔루션- ( scoffey )는 Python 2.6에서도 작동합니다. 결과 해석은 질문의 요구 사항에 맞게 개선 될 수 있지만,
result
tearDown 호출 후에 업데이트 되기 때문에 Python 3.4 이상에서는 아무것도 작동 하지 않습니다. - Mark G .: (Python 2.7, 3.2, 3.3, 3.4 및 nosetest로 테스트 됨)
- ( scoffey )는 Python 2.6에서도 작동합니다. 결과 해석은 질문의 요구 사항에 맞게 개선 될 수 있지만,
exc_info()
(Pavel Repin 2st way)의 솔루션은 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 커밋 메시지 읽기) : 테스트 결과가 테스트와 많이 분리되는 이유 는 메모리 누수 방지입니다. 모든 예외 정보는 모든 로컬 변수를 포함하여 실패한 프로세스 상태의 프레임에 액세스 할 수 있습니다. 또한 실패 할 수있는 코드 블록의 로컬 변수에 프레임이 할당되면 교차 메모리 참조가 쉽게 생성 될 수 있습니다. 가비지 수집기 덕분에 끔찍하지는 않지만 사용 가능한 메모리는 메모리가 올바르게 해제되는 것보다 더 빨리 조각화 될 수 있습니다. 이것이 예외 정보와 트레이스 백이 곧 문자열로 변환되는 이유와 같은 임시 객체 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.__name__ = 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
를 통해 실패한 테스트를 감지 한다는 사실에 의존합니다 . 지금까지 내가 아는 유일한 특별한 요구가 처리 할 수 있다는 예외입니다 (테스트 실패를 표시하지 않는). 다른 모든 예외는 테스트 실패를 나타내므로 우리에게 버블 링 될 때이를 표시합니다.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')
이것이 제대로 처리되지 않는 경우가 많이 있습니다. 예를 들어 실패한 하위 테스트 또는 예상 실패를 올바르게 감지하지 못합니다 . 이 문제의 다른 실패 모드에 관심이 있으므로 내가 제대로 처리하지 못하는 경우를 발견하면 의견으로 알려 주시면 조사하겠습니다.
* 더 쉬운 방법이 없었다면 _tag_error
개인 기능을 만들지 않았을 것입니다 ;-)
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
현재 테스트의 이름은 unittest.TestCase.id () 메소드 로 검색 할 수 있습니다 . 따라서 tearDown에서 self.id ()를 확인할 수 있습니다.
예는 다음 방법을 보여줍니다.
- 현재 테스트에 오류 또는 실패 목록이 있는지 확인
- PASS 또는 FAIL 또는 EXCEPTION으로 테스트 ID 인쇄
여기에서 테스트 된 예제는 @scoffey의 좋은 예제와 함께 작동합니다.
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)
scoffey의 대답에 영감 을 받아 무자비한 것을 다음 단계 로 끌어 올리기 로 결심 하고 다음을 생각해 냈습니다.
바닐라 unittest와 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 __name__ == '__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 -----
배경
나는 이것으로 시작 했다.
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 패키지 는 각 테스트의 메서드를 호출 한 후 결과 를 처리합니다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 __name__ == '__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 은 테스트가 해체 된 후 오류 / 실패를 처리하기 때문에 result.errors
또는 result.failures
모든 경우를 사용하여 테스트 결과를 쉽게 추론 할 수 없습니다 . (나는 아마 테스트의 결과를 처리하기 위해 구조적으로 더 의미가 생각 후에 그러나, 그것은, 그것을 아래로 찢어 않습니다 / 테스트의 통과에 따라 다른 끝 테스트 절차를 다음의 완벽하게 유효한 사용 사례를 만들 상태를 조금 실패 만나기가 더 힘들어…)
따라서, 대신 전체에 의존하는 result
대신에 우리가 참조 할 수있는 개체 _outcomeForDoCleanups
로 다른 사람이 한 이미 현재 실행중인 테스트에 대한 결과 개체를 포함하는, 언급하고, 필요를 가지고 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의 초기 버전에 대한 지원이 추가됩니다.
파이썬 3.4로, 그러나,이 개인 멤버 변수는 더 이상 존재하지 않습니다 , 대신, (이기는하지만 새로운 도 방법 개인)이 추가되었습니다 _feedErrorsToResult
.
This means that for versions 3.4 (and later), if the need is great enough, one can — very hackishly — force one’s way in to make it all work again like it did in version 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 -----')
…provided, of course, all consumers of this class remember to super(…, self).tearDown()
in their respective tearDown
methods…
Disclaimer: Purely educational, don’t try this at home, etc. etc. etc. I’m not particularly proud of this solution, but it seems to work well enough for the time being, and is the best I could hack up after fiddling for an hour or two on a Saturday afternoon…
Tested for python 3.7 - sample code for getting info of failing assertion but can give idea how to deal with errors:
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)
참고URL : https://stackoverflow.com/questions/4414234/getting-pythons-unittest-results-in-a-teardown-method
'Program Tip' 카테고리의 다른 글
git ls-files : 새 파일을 식별하는 방법 (추가됨, 커밋되지 않음)? (0) | 2020.11.27 |
---|---|
Makefile : 문자열 포함 (0) | 2020.11.27 |
Log4j : 가능한 가장 간단한 파일 로깅을 구성하는 방법은 무엇입니까? (0) | 2020.11.27 |
NSUserDefaults에 값을 저장하는 데 제한이 있습니까? (0) | 2020.11.27 |
같음 연산에서 "SQL_Latin1_General_CP1_CI_AS"와 "Latin1_General_CI_AI"간의 데이터 정렬 충돌을 해결할 수 없습니다. (0) | 2020.11.27 |