在单元测试中哪里可以捕获 KeyboardInterrupt?
Where to catch a KeyboardInterrupt in unittests?
我正在 UI 使用 Appium 服务器在移动设备上进行测试。我希望能够在开发测试时取消测试过程。目前,当我 CTRL-C
退出进程(python 单元测试)时,我必须重新启动 appium 服务器,因为会话没有正确关闭(这将在 [=12] =] 测试方法,但是因为我按下 CTRL-C,所以不会执行。)相反,我想在每次测试被 KeyboardInterrupt
.[=21 取消时触发 tearDown()
=]
现在我的问题是:我应该将 try-catch 块放在哪里来实现这一点?在 Python 单元测试中是否有处理此问题的最佳实践?我需要在 KeyboardInterrupt
触发后立即访问 class 变量 (self.driver.quit()
)。 class 变量位于 class 中,该 class 被放入 unittest.TestSuite
中,而 unittest.TextTestRunner
是 运行。
try:
self.test_something()
except KeyboadInterrupt:
self.driver.quit()
我研究了 unittest.TestResult
及其 stop()
方法,但没有找到正确解释其用法的实际示例。
您可以更改 Python 关于 SIGINT 的默认行为以满足您的需要:
import signal
def my_ctrlc_handler(signal, frame):
driver_class.quit()
raise KeyboardInterrupt
signal.signal(signal.SIGINT, my_ctrlc_handler)
我制作了一个自定义测试用例,确保 tearDown()
、tearDownClass()
和 tearDownModule()
都是 运行,如果使用 Ctrl-C
取消了测试。它还确保 tearDown()
等 all 运行 if any 在 setUp()
.
中引发异常
代码
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 方法必须是幂等的。有可能是拆卸步骤
将 运行 没有相应的设置 运行.
- 如果在 SetUpClass 或 SetUpModule 中引发异常,tearDowns 将
不是 运行。 (解决这个问题涉及制作一个自定义的 TestSuite,它
如果您 运行 通过 PyCharm 测试,将被忽略。)
我正在 UI 使用 Appium 服务器在移动设备上进行测试。我希望能够在开发测试时取消测试过程。目前,当我 CTRL-C
退出进程(python 单元测试)时,我必须重新启动 appium 服务器,因为会话没有正确关闭(这将在 [=12] =] 测试方法,但是因为我按下 CTRL-C,所以不会执行。)相反,我想在每次测试被 KeyboardInterrupt
.[=21 取消时触发 tearDown()
=]
现在我的问题是:我应该将 try-catch 块放在哪里来实现这一点?在 Python 单元测试中是否有处理此问题的最佳实践?我需要在 KeyboardInterrupt
触发后立即访问 class 变量 (self.driver.quit()
)。 class 变量位于 class 中,该 class 被放入 unittest.TestSuite
中,而 unittest.TextTestRunner
是 运行。
try:
self.test_something()
except KeyboadInterrupt:
self.driver.quit()
我研究了 unittest.TestResult
及其 stop()
方法,但没有找到正确解释其用法的实际示例。
您可以更改 Python 关于 SIGINT 的默认行为以满足您的需要:
import signal
def my_ctrlc_handler(signal, frame):
driver_class.quit()
raise KeyboardInterrupt
signal.signal(signal.SIGINT, my_ctrlc_handler)
我制作了一个自定义测试用例,确保 tearDown()
、tearDownClass()
和 tearDownModule()
都是 运行,如果使用 Ctrl-C
取消了测试。它还确保 tearDown()
等 all 运行 if any 在 setUp()
.
代码
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 方法必须是幂等的。有可能是拆卸步骤 将 运行 没有相应的设置 运行.
- 如果在 SetUpClass 或 SetUpModule 中引发异常,tearDowns 将 不是 运行。 (解决这个问题涉及制作一个自定义的 TestSuite,它 如果您 运行 通过 PyCharm 测试,将被忽略。)