我正在扩展python 2.7 unittest
框架以进行一些功能测试。我想做的一件事就是阻止所有测试在测试内部运行,并且在setUpClass()
方法内部。有时如果测试失败,程序就会被破坏,继续测试不再有任何用处,所以我想阻止测试运行。
我注意到TestResult具有shouldStop
属性和stop()
方法,但我不确定如何访问测试内部。
有没有人有任何想法?还有更好的方法吗?
答案 0 :(得分:14)
如果您感兴趣,这里有一个简单的示例,您可以自己决定如何使用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次失败后实施停止。
Sidenote2:py.test
对以传统unittest.TestCase
样式运行测试的支持有限。
答案 1 :(得分:4)
这是我在一段时间后提出的另一个答案:
首先,我添加了一个新的例外:
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 :(得分:3)
目前,您只能在套件级别停止测试。进入TestCase
后,在迭代测试时不会使用stop()
的{{1}}方法。
与您的问题有些相关,如果您使用的是python 2.7,则可以在使用TestResult
调用测试时使用-f/--failfast
标记。这将在第一次失败时停止测试。
请参阅25.3.2.1. failfast, catch and buffer command line options
答案 3 :(得分:1)
在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)
我不确定是否有更好的方法,但我的测试表现正确。
答案 4 :(得分:1)
OP是关于python 2.7的。跳过十年,python 3.1 and above, the way to skip tests in python unittest has had an upgrade,但文档可能需要一些澄清(IMHO):
文档涵盖以下内容:
failfast
(仅在您真的根本不想继续进行任何进一步测试(包括其他不相关的TestCase类)时才有用)@unittest.skip()
等修饰类。@unittest.skip()
等修饰方法。@unittest.skipIf()
或@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)
答案 5 :(得分:0)
我查看了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 :(得分:0)
虽然你不会得到目前为止运行的测试的常用测试报告,但是在TestCase
方法中停止测试运行的一种非常简单的方法就是在方法内部引发KeyboardInterrupt
通过查看KeyboardInterrupt
内部的CPython代码here,您可以看到unittest
只允许在testPartExecutor()
的测试跑步者中冒出来。
答案 7 :(得分:0)
使用:
if condition:
return 'pass'