Действительно ли возможно получить результаты теста (т.е. передали ли все утверждения) в разрушении () метод? Я запускаю скрипты Селена, и я хотел бы сделать некоторое создание отчетов из разрушения (), однако я не знаю, возможно ли это.
Вот решение для тех из нас, кто неудобные решения для использования, которые полагаются 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
обнаруживает проваленные тесты через [1 111] Исключения . Насколько я знаю, только [1 112] особенные исключение, которое должно быть обработано, 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 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)
Это решение для [1 145] версии Python 2.7 к 3,7 (самая высокая текущая версия) без любых декораторов или другой модификации в любом коде прежде tearDown
. Все работает согласно встроенной классификации результатов. Также пропущенные тесты или expectedFailure
распознаны правильно. Это оценивает результат текущего теста, не, сводка всех тестов передала до сих пор. Совместимый также с [1 116] 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
. Пакет unittest
ожидает, что второе исключение может быть повышено разрушением. Поэтому списки errors
и failures
могут содержать только элементы единицы или нули вместе перед разрушением. Строки после "демонстрационного" комментария сообщают о коротком результате.
Демонстрационный вывод: (не важный)
$ 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)
<час> Comparision к другим решениям - (относительно истории фиксации исходного репозитория Python):
Это решение использует частный атрибут из экземпляра TestCase как много других решений, но я проверил тщательно все соответствующие фиксации в исходный репозиторий Python, что три альтернативных имени освещают историю кода начиная с Python 2.7 к 3.6.2 без любого разрыва. Это может быть проблема после некоторого нового выпуска майора Python, но это могло быть ясно распознано, пропущено и легко зафиксировано позже для нового Python. Преимущество состоит в том, что ничто не изменяется перед рабочим разрушением это никогда не должно повреждать тест, и вся функциональность unittest поддерживается, работы с pytest, и это могло работать много расширяющихся пакетов, но не с nosetest (не, удивление becase nosetest не совместимо, например, с unittest.expectedFailure).
решения с [1 123] декораторы на пользовательских методах тестирования или со специализированным failureException ( mgilson, Pavel Repin 2-й путь, kenorb) устойчив против будущих версий Python, но если бы все должно работать полностью, они выросли бы как шар снега за более поддерживаемыми исключениями и более дублируемыми внутренностями unittest. Украшенные функции имеют меньше читаемого tracebacks (еще больше уровней, добавленных одним декоратором), они более сложны для отладки, и неприятно, если у другого более важного декоратора есть проблема. (Благодаря mgilson основная функциональность готова, и известные проблемы могут быть устранены.)
решение с методом modifired run
и пойманный result
параметр
result
обновляется после вызова разрушения, никогда прежде. решение [1 113] (Pavel Repin 2 путей) работы только с Python 2.
Другие решения principially аналогичны, но меньше завершенное или с большим количеством недостатков.
Объясненный исходным репозиторием 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): причина , почему результаты испытаний так отделяются от тестов, утечки памяти предотвращение. Каждая информация об исключении может получить доступ к кадрам неудавшегося состояния процесса включая все локальные переменные. Если кадр присвоен локальной переменной в блоке кода, который мог бы также перестать работать, то перекрестный перезабор памяти мог быть легко создан. Это не ужасно благодаря сборщику "мусора", но свободная память может, стал фрагментированным более быстро, чем если бы память будет освобождена правильно. Это - причина, почему информация об исключении и traceback преобразовываются очень скоро в строки и почему временные объекты как [1 114] инкапсулируются и не установлены ни на Один в finally
блок, чтобы к утечкам памяти предотвращены.
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 () метод. Таким образом в разрушении можно проверить self.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)
Вдохновленный [1 123] ответ scoffey’s , я решил взять беспощадность к следующему уровню и придумал следующее.
Это работает и в ванили unittest, и в также, когда выполнено через nosetests, и также работает в версиях 2.7, 3.2, 3.3 Python, и 3.4 (я конкретно не протестировал 3.0, 3.1, или 3.5, как я, don’t установили их в данный момент, но если бы я читал исходный код правильно, то это должно работать в 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()
, Когда выполнено с [1 110]:
$ ./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 -----
, Когда выполнено с [1 111]:
$ 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’s unittest пакет результаты процессов после вызов каждого test’s tearDown()
method†¦, это поведение может быть подтверждено, если мы просто добавляем дополнительную строку (или шесть) к нашему тестовому классу:
@@ -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’s:
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 обрабатывает ошибки/отказы после , тест разъединяется, мы, can’t с готовностью выводят результат теста с помощью [1 113] или result.failures
в каждом случае. (Я думаю, что, вероятно, имеет больше смысла архитектурно обрабатывать результаты test’s после разъединение его, однако, это делает , делают совершенно допустимый пример использования следующих другим концом процедуры тестирования в зависимости от состояния передачи/сбоя test’s немного тяжелее к meet†¦)
Поэтому вместо того, чтобы полагаться на полное result
объект, вместо этого мы можем сослаться _outcomeForDoCleanups
как [1 126], другие уже имеют упомянутый, который содержит объект результата для в настоящее время запускающего теста и имеет необходимое errors
и failrues
атрибуты, которые мы можем использовать для выведения test’s состояния к тому времени, когда 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, однако, эта переменная члена парламента, не занимающего официального поста больше не существует , и вместо этого, новое (хотя также частный), метод был добавлен: _feedErrorsToResult
.
Это означает, что для версий 3.4 ( и позже ), если потребность является достаточно большой, каждый может — очень hackishly — сила one’s путь в сделать, все это работать снова как он сделало в версии 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 -----')
†¦, если, , конечно, все потребители этого класса помнят к [1 121] в их соответствующем tearDown
methods†¦
Правовая оговорка: Чисто образовательный, don’t пробуют это дома, и т.д. и т.д. и т.д. I’m, не особенно гордящийся этим решением, но это, кажется, работает достаточно хорошо в настоящее время и является лучшим, чтобы я мог изрубить после игры в течение часа или два в субботу afternoon†¦
Это зависит, какое создание отчетов требуется произвести.
В случае, если требуется сделать некоторые действия с отказом (такой как генерация снимки экрана ), вместо того, чтобы использовать 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