繁体   English   中英

在单元测试中,哪里可以捕捉到KeyboardInterrupt?

[英]Where to catch a KeyboardInterrupt in unittests?

我正在使用Appium服务器在移动设备上进行UI测试。 我希望能够在开发测试时取消测试过程。 当前,当我CTRL-C退出进程(python unittest)时,我必须重新启动appium服务器,因为会话未正确关闭(这可以在测试的tearDown()方法中完成,但是(因为我按了CTRL-C,所以将不会执行该操作。)相反,我想每次通过KeyboardInterrupt取消测试时都将tearDown()触发。

现在的问题是:在哪里放置try-catch块来实现这一目标? 有没有在Python单元测试中处理此问题的最佳实践? KeyboardInterrupt触发后,我需要访问一个类变量( self.driver.quit() )。 该类变量位于unittest.TestSuite ,该类位于unittest.TextTestRunner正在运行的内部。

try:
  self.test_something()
except KeyboadInterrupt:
  self.driver.quit()

我已经研究了unittest.TestResult及其stop()方法,但是还没有找到正确解释其用法的实际示例。

您可以更改有关SIGINT的Python的默认行为,以适合您的需求:

import signal

def my_ctrlc_handler(signal, frame):
    driver_class.quit()
    raise KeyboardInterrupt

signal.signal(signal.SIGINT, my_ctrlc_handler)

我制作了一个自定义的TestCase,以确保如果使用Ctrl-C取消了测试,则都可以运行tearDown()tearDownClass()tearDownModule() 如果setUp()引发任何异常,它还可以确保tearDown()等全部运行。

class SafeTestCase(TestCase):
    """
    SafeTestCase makes sure that tearDown / cleanup methods are always run when
    They should be.
    """

    def run(self, result=None):
        test_method = getattr(self, self._testMethodName)
        wrapped_test = self._cleanup_wrapper(test_method, KeyboardInterrupt)
        setattr(self, self._testMethodName, wrapped_test)

        self.setUp = self._cleanup_wrapper(self.setUp, BaseException)

        return super().run(result)

    def _cleanup_wrapper(self, method, exception):
        def wrapped(*args, **kwargs):
            try:
                return method(*args, **kwargs)
            except exception:
                self.tearDown()
                self.doCleanups()
                raise

        return wrapped

用法

要使用此功能,只需确保您的TestCase继承自SafeTestCase而不是unittest.TestCase

class MyTestCase(SafeTestCase):

    @classmethod
    def setUpClass(cls) -> None:
        print('setUpClass entered')
        print('setUpClass exited')

    @classmethod
    def tearDownClass(cls) -> None:
        print('tearDownClass entered')
        print('tearDownClass exited')

    def setUp(self) -> None:
        print('setUp entered')
        # raise ValueError()  # tearDown() and tearDownClass() will still run
        print('setUp exited')

    def tearDown(self):
        print('tearDown entered')
        foo = 1
        print('tearDown exited')

    def test_test(self):
        print('test entered')
        raise KeyboardInterrupt()   # tearDown() and tearDownClass() will still run
        print('test exited')

将打印类似

setUpClass entered
setUpClass exited
setUp entered
setUp exited
test entered
tearDown entered
tearDown exited
tearDownClass entered
tearDownClass exited

并切换注释哪个异常将打印类似

setUpClass entered
setUpClass exited
setUp entered
tearDown entered
tearDown exited
tearDownClass entered
tearDownClass exited

注意事项

  • 所有的tearDown方法必须是幂等的。 如果没有运行相应的setUp,则tearDown步骤可能会运行。
  • 如果SetUpClass或SetUpModule中引发异常,则将不会运行tearDowns。 (解决此问题涉及制作自定义TestSuite,如果通过PyCharm运行测试,则将忽略该自定义。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM