Как остановить все тесты из теста или setUp с помощью unittest?
Я расширяю рамки python 2.7 unittest
, чтобы выполнить некоторое тестирование функции. Одна из вещей, которые я хотел бы сделать, - остановить все тесты от запуска внутри теста и внутри метода setUpClass()
. Иногда, если тест терпит неудачу, программа настолько сломана, что больше не нужно проводить тестирование, поэтому я хочу остановить выполнение тестов.
Я заметил, что TestResult имеет атрибут shouldStop
и метод stop()
, но я не уверен, как получить доступ к этому тесту.
Есть ли у кого-нибудь идеи? Есть ли лучший способ?
Ответы
Ответ 1
Вот еще один ответ, который я придумал через некоторое время:
Во-первых, я добавил новое исключение:
class StopTests(Exception):
"""
Raise this exception in a test to stop the test run.
"""
pass
то я добавил новый assert
в мой тестовый класс:
def assertStopTestsIfFalse(self, statement, reason=''):
try:
assert statement
except AssertionError:
result.addFailure(self, sys.exc_info())
и, наконец, я переопределил функцию run
, чтобы включить это прямо под вызовом testMethod()
:
except StopTests:
result.addFailure(self, sys.exc_info())
result.stop()
Мне нравится это лучше, так как любой тест теперь имеет возможность остановить все тесты, и нет кода, специфичного для cpython.
Ответ 2
В случае, если вам интересно, вот простой пример того, как вы могли сами принять решение о выходе из тестового набора с помощью py.test:
# content of test_module.py
import pytest
counter = 0
def setup_function(func):
global counter
counter += 1
if counter >=3:
pytest.exit("decided to stop the test run")
def test_one():
pass
def test_two():
pass
def test_three():
pass
и если вы запустите это, вы получите:
$ pytest test_module.py
============== test session starts =================
platform linux2 -- Python 2.6.5 -- pytest-1.4.0a1
test path 1: test_module.py
test_module.py ..
!!!! Exit: decided to stop the test run !!!!!!!!!!!!
============= 2 passed in 0.08 seconds =============
Вы также можете поместить вызов py.test.exit()
внутри теста или в плагин, зависящий от проекта.
Sidenote: py.test
поддерживает py.test --maxfail=NUM
для поддержки остановки после NUM-сбоев.
Sidenote2: py.test
имеет ограниченную поддержку для запуска тестов в традиционном стиле unittest.TestCase
.
Ответ 3
В настоящее время вы можете остановить тесты только на уровне набора. Когда вы находитесь в TestCase
, метод stop()
для TestResult
не используется при повторном тестировании.
В некотором отношении к вашему вопросу, если вы используете python 2.7, вы можете использовать флаг -f/--failfast
при вызове вашего теста с помощью python -m unittest
. Это остановит тест при первом сбое.
См. 25.3.2.1. параметры командной строки failfast, catch и buffer
Вы также можете использовать Nose для запуска своих тестов и использовать -x, --stop
флаг, чтобы остановить тест раньше.
Ответ 4
В тестовом цикле unittest.TestSuite
в начале есть условие break
:
class TestSuite(BaseTestSuite):
def run(self, result, debug=False):
topLevel = False
if getattr(result, '_testRunEntered', False) is False:
result._testRunEntered = topLevel = True
for test in self:
if result.shouldStop:
break
Итак, я использую собственный тестовый набор:
class CustomTestSuite(unittest.TestSuite):
""" This variant registers the test result object with all ScriptedTests,
so that a failed Loign test can abort the test suite by setting result.shouldStop
to True
"""
def run(self, result, debug=False):
for test in self._tests:
test.result = result
return super(CustomTestSuite, self).run(result, debug)
с помощью собственного класса результатов теста:
class CustomTestResult(TextTestResult):
def __init__(self, stream, descriptions, verbosity):
super(CustomTestResult, self).__init__(stream, descriptions, verbosity)
self.verbosity = verbosity
self.shouldStop = False
и мои тестовые классы:
class ScriptedTest(unittest.TestCase):
def __init__(self, environment, test_cfg, module, test):
super(ScriptedTest, self).__init__()
self.result = None
При определенных условиях я прерываю набор тестов; например, набор тестов начинается с входа в систему, и если это не удается, мне не нужно пробовать остальные:
try:
test_case.execute_script(test_command_list)
except AssertionError as e:
if test_case.module == 'session' and test_case.test == 'Login':
test_case.result.shouldStop = True
raise TestFatal('Login failed, aborting test.')
else:
raise sys.exc_info()
Затем я использую набор тестов следующим образом:
suite = CustomTestSuite()
self.add_tests(suite)
result = unittest.TextTestRunner(verbosity=self.environment.verbosity, stream=UnitTestLoggerStream(self.logger),
resultclass=CustomTestResult).run(suite)
Я не уверен, есть ли лучший способ сделать это, но он ведет себя правильно для моих тестов.
Ответ 5
Я просмотрел класс TestCase
и решил подклассифицировать его. Класс просто переопределяет run()
. Я скопировал метод и, начиная с строки 318 в исходном классе, добавил следующее:
# this is CPython specific. Jython and IronPython may do this differently
if testMethod.func_code.co_argcount == 2:
testMethod(result)
else:
testMethod()
В нем есть определенный код CPython, чтобы определить, может ли тестовый метод принять другой параметр, но поскольку я использую CPython всюду, это не проблема для меня.
Ответ 6
Хотя вы не получите обычные тестовые отчеты об испытаниях, запущенных до сих пор, очень простой способ остановить тестовый прогон из метода TestCase
- просто поднять KeyboardInterrupt
внутри метода.
Вы можете видеть, как только KeyboardInterrupt
разрешается всплывать внутри unittest
тестового бегуна, просматривая код CPython здесь внутри testPartExecutor()
.