【问题标题】:How to stop all tests from inside a test or setUp using unittest?如何使用 unittest 从测试或设置中停止所有测试?
【发布时间】:2011-04-17 22:15:52
【问题描述】:

我正在扩展 python 2.7 unittest 框架来做一些功能测试。我想做的一件事是阻止所有测试在测试内部和setUpClass() 方法内部运行。有时如果一个测试失败了,程序就这么坏了,不再继续测试了,所以我想停止运行测试。

我注意到 TestResult 有一个 shouldStop 属性和一个 stop() 方法,但我不确定如何在测试中访问它。

有人有什么想法吗?有没有更好的办法?

【问题讨论】:

  • 如果被测应用程序配置了 prod 设置,我会使用它在运行任何测试之前中止测试运行。 (实际上,任何非测试设置。)

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


【解决方案1】:

目前,您只能在套件级别停止测试。一旦您进入TestCase,在迭代测试时不会使用TestResultstop() 方法。

与您的问题有些相关,如果您使用的是 python 2.7,则可以在使用 python -m unittest 调用测试时使用 -f/--failfast 标志。这将在第一次失败时停止测试。

25.3.2.1. failfast, catch and buffer command line options

您还可以考虑使用Nose 运行测试并使用-x, --stop flag 提前停止测试。

【讨论】:

  • 谢谢。我看到了 failfast 选项,但我并不总是希望在第一个错误上失败,只是在选定的地方。我想我应该编辑我的问题以提及我正在使用 python 2.7。
【解决方案2】:

我查看了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,这对我来说不是问题。

【讨论】:

    【解决方案3】:

    如果您有兴趣,这里有一个简单的示例,您可以自己决定如何使用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 样式运行测试提供有限支持。

    【讨论】:

    • 谢谢。我不知道那个测试框架。我去看看。
    • 当前版本的 Py.Test: import pytest 然后你可以做 pytest.exit("your message")
    • 这也适用于在 conftest.py 文件中声明的 pytest 夹具,scope='session',autouse=True。这将在项目中的每个测试之前运行。从这里调用 pytest.exit 可以在任何测试运行之前中止整个测试运行。如果测试正在使用 prod 配置(实际上是任何非测试配置)运行,我会使用它来中止测试
    【解决方案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 的代码。

    【讨论】:

    • StopTests 在哪里提出?
    【解决方案5】:

    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)
    

    我不确定是否有更好的方法,但它在我的测试中表现正确。

    【讨论】:

      【解决方案6】:

      虽然到目前为止您不会获得测试运行的常规测试报告,但从 TestCase 方法中停止测试运​​行的一个非常简单的方法是在方法内引发 KeyboardInterrupt

      您可以通过查看 testPartExecutor() 中的 CPython 代码 here 来了解仅允许 KeyboardInterruptunittest 的测试运行程序中冒泡。

      【讨论】:

      • 我能够通过在我的setUp() 方法中引发一个自定义的 KeyboardInterrupt 子类来中止所有其他测试,然后在我的run() 方法中使用 result.stop() 处理它。内幕交易有罪,但有助于在数据库关闭时将数百条错误消息减少到一条。
      【解决方案7】:

      用途:

      if condition: 
         return 'pass'
      

      【讨论】:

      • 一个非常糟糕的答案,没有解释,老实说,我不明白它是如何做到 OP 要求的。
      【解决方案8】:

      OP 是关于 python 2.7 的。跳过十年,对于python 3.1 and above, the way to skip tests in python unittest has had an upgrade,但文档可能需要一些说明(恕我直言):

      文档涵盖以下内容:

      • 第一次失败后跳过所有测试:使用 failfast(仅当您真的不想继续任何进一步的测试时有用,包括在其他不相关的 TestCase 类中)
      • 跳过 TestCase 类中的所有测试:使用 @unittest.skip() 等装饰类。
      • 跳过 TestCase 中的单个方法:使用 @unittest.skip() 等装饰方法。
      • 有条件地跳过方法或类:用@unittest.skipIf()@unittest.skipUnless() 等装饰。
      • 有条件地跳过一个方法,但直到该方法中的某些内容运行时才开始:在方法中使用 self.skipTest()(这将跳过该方法,并且仅跳过该方法,而不是后续方法)

      文档不包括以下内容(截至撰写本文时):

      1. 如果在 setUpClass 方法中满足条件,则跳过 TestCase 类中的所有测试:solution from this postraise 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)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-09-16
        • 1970-01-01
        • 2013-03-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-11
        • 1970-01-01
        相关资源
        最近更新 更多