28

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

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

有没有人有任何想法?有没有更好的办法?

4

8 回答 8

27

如果您有兴趣,这里有一个简单的示例,您可以自己决定如何使用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

于 2010-09-29T11:46:55.270 回答
5

这是我一段时间后想出的另一个答案:

首先,我添加了一个新的例外:

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 的代码。

于 2012-02-03T00:22:46.467 回答
4

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

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

25.3.2.1。failfast、catch 和 buffer 命令行选项

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

于 2010-09-27T19:58:21.303 回答
2

在 的测试循环中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)

我不确定是否有更好的方法可以做到这一点,但它在我的测试中表现正确。

于 2014-06-10T13:01:36.677 回答
2

OP是关于python 2.7的。跳过十年,对于python 3.1 及更高版本,在 python unittest 中跳过测试的方式已经升级,但文档可以使用一些澄清(恕我直言):

该文档涵盖以下内容:

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

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

  1. 如果在 setUpClass 方法中满足条件,则跳过 TestCase 类中的所有测试: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)
于 2020-11-19T22:22:00.733 回答
2

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

通过查看这里的 CPython 代码,您可以了解如何只KeyboardInterrupt允许在测试运行器内部冒泡。unittesttestPartExecutor()

于 2017-08-05T04:55:13.147 回答
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,这对我来说不是问题。

于 2010-09-28T18:03:08.500 回答
-3

利用:

if condition: 
   return 'pass'
于 2018-10-23T22:30:07.180 回答