如何使用unittest停止测试或setUp内的所有测试?

时间:2010-09-27 18:31:19

标签: python testing automated-tests functional-testing python-unittest

我正在扩展python 2.7 unittest框架以进行一些功能测试。我想做的一件事就是阻止所有测试在测试内部运行,并且在setUpClass()方法内部。有时如果测试失败,程序就会被破坏,继续测试不再有任何用处,所以我想阻止测试运行。

我注意到TestResult具有shouldStop属性和stop()方法,但我不确定如何访问测试内部。

有没有人有任何想法?还有更好的方法吗?

8 个答案:

答案 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

您还可以考虑使用Nose来运行测试,并使用python -m unittest flag提前停止测试。

答案 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类)时才有用)
  • 跳过所有TestCase类中的测试:用@unittest.skip()等修饰类。
  • 跳过TestCase中的单个方法:用@unittest.skip()等修饰方法。
  • 有条件地跳过方法或类:用@unittest.skipIf()@unittest.skipUnless()等修饰。
  • 有条件地跳过一个方法,但是要等到该方法中的某个东西运行后再使用:在方法内部使用self.skipTest()(这将跳过该方法,并且仅跳过该方法,而不是后续方法)

(本文撰写时)文档不涉及以下内容:

  1. 如果在setUpClass方法内满足条件,则跳过TestCase类中的所有测试:solution from this post raise unittest.SkipTest("skip all tests in this class")(可能有另一种方法,但我不知道)
  2. 在第一个测试之一满足条件后,跳过所有后续的TestCase类中的测试方法,但仍继续测试其他不相关的TestCase类。为此,我提出以下解决方案...

此解决方案假定您在测试方法的中间遇到了“不良状态”,并且只能在测试方法中注意到(即,在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'