我正在扩展 python 2.7unittest
框架来做一些功能测试。我想做的一件事是阻止所有测试在测试内部和setUpClass()
方法内部运行。有时,如果测试失败,程序已经损坏,不再继续测试,所以我想停止运行测试。
我注意到 TestResult 有一个shouldStop
属性和一个stop()
方法,但我不确定如何在测试中访问它。
有没有人有任何想法?有没有更好的办法?
我正在扩展 python 2.7unittest
框架来做一些功能测试。我想做的一件事是阻止所有测试在测试内部和setUpClass()
方法内部运行。有时,如果测试失败,程序已经损坏,不再继续测试,所以我想停止运行测试。
我注意到 TestResult 有一个shouldStop
属性和一个stop()
方法,但我不确定如何在测试中访问它。
有没有人有任何想法?有没有更好的办法?
如果您有兴趣,这里有一个简单的示例,您可以自己决定如何使用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()
调用放在测试中或特定于项目的插件中。
旁注:py.test
原生支持py.test --maxfail=NUM
实现 NUM 失败后停止。
旁注 2:py.test
仅对以传统方式运行测试提供有限支持unittest.TestCase
。
这是我一段时间后想出的另一个答案:
首先,我添加了一个新的例外:
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 的代码。
目前,您只能在套件级别停止测试。一旦进入 a TestCase
,迭代测试时就不会使用的stop()
方法。TestResult
与您的问题有些相关,如果您使用的是 python 2.7,则可以在-f/--failfast
使用python -m unittest
. 这将在第一次失败时停止测试。
在 的测试循环中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)
我不确定是否有更好的方法可以做到这一点,但它在我的测试中表现正确。
OP是关于python 2.7的。跳过十年,对于python 3.1 及更高版本,在 python unittest 中跳过测试的方式已经升级,但文档可以使用一些澄清(恕我直言):
该文档涵盖以下内容:
failfast
(仅当您真的不想继续任何进一步的测试时才有用,包括在其他不相关的 TestCase 类中)@unittest.skip()
跳过 TestCase 类中的所有测试:用等装饰类。@unittest.skip()
等装饰方法。@unittest.skipIf()
or@unittest.skipUnless()
等装饰。self.skipTest()
方法内使用(这将跳过该方法,并且仅跳过该方法,而不是后续方法)该文档不包括以下内容(截至撰写本文时):
raise unittest.SkipTest("skip all tests in this class")
(可能有另一种方法,但我不知道)此解决方案假定您在测试方法的中间遇到“不良状态”,并且只能在测试方法中注意到(即,无论出于何种原因,它都不能在 setUpClass 方法中确定) )。事实上,setUpClass 方法是在初始条件不正确时确定是否继续的最佳位置,但有时(正如我所遇到的)你只是在运行一些测试方法之前不知道。此解决方案假定测试方法按字母顺序排列,并且您在遇到“坏”状态后不想运行的后续测试方法按字母顺序排列。
import unittest
class SkipMethodsConditionally(unittest.TestCase):
@classmethod
def setUpClass(cls):
#this class variable maintains whether or not test methods should continue
cls.should_continue = True
#this class variable represents the state of your system. Replace with function of your own
cls.some_bad_condition = False
def setUp(self) -> None:
"""setUp runs before every single test method in this class"""
if not self.__class__.should_continue:
self.skipTest("no reason to go on.")
def test_1_fail(self):
#Do some work here. Let's assume you encounter a "bad state,"" that could
#only be noticed in this first test method only, (i.e., it's not something that
#can be placed in the setUpClass method, for whatever reason)
self.__class__.some_bad_condition = True
if self.__class__.some_bad_condition:
self.__class__.should_continue = False
self.assertTrue(False,"this test should fail, rendering the rest of the tests irrelevant")
def test_2_pass(self):
self.assertFalse(self.__class__.some_bad_condition,"this test would pass normally if run, but should be skipped, because it would fail")
上述测试将产生以下输出:
test_1_fail (__main__.SkipMethodsConditionally) ... FAIL
test_2_pass (__main__.SkipMethodsConditionally) ... skipped 'no reason to go on.'
----------------------------------------------------------------------
Ran 2 tests in 0.001s
FAILED (failures=1, skipped=1)
尽管到目前为止您不会获得测试运行的常规测试报告,但是从方法内停止测试运行的一种非常简单的TestCase
方法就是在方法内引发KeyboardInterrupt
。
通过查看这里的 CPython 代码,您可以了解如何只KeyboardInterrupt
允许在测试运行器内部冒泡。unittest
testPartExecutor()
我查看了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,这对我来说不是问题。
利用:
if condition:
return 'pass'