我正在使用
python
框架unittest
。是否可以通过框架的能力指定测试超时?如果不是,是否可以为所有测试优雅地指定 timeout
并为某些单独的测试指定每个测试的私有值?global timeout
(默认情况下他们将使用它),并为某些可能需要很长时间的测试定义超时。
unittest
不包含任何对测试超时的支持。
timeout-decorator
库。将装饰器应用于各个测试,以使其在花费太长时间时终止:
import timeout_decorator
class TestCaseWithTimeouts(unittest.TestCase):
# ... whatever ...
@timeout_decorator.timeout(LOCAL_TIMEOUT)
def test_that_can_take_too_long(self):
sleep(float('inf'))
# ... whatever else ...
要创建全局超时,可以替换call
unittest.main()
与
timeout_decorator.timeout(GLOBAL_TIMEOUT)(unittest.main)()
我使用上下文管理器(
unittest
关键字)构建了一个with
超时解决方案,基于这个答案。
这种方法也使用了
signal
,所以它可能只在 *nix 系统上有效(我只在我的 Ubuntu 16.04 环境中运行过它)。
TestTimeout
例外:import signal
...
class TestTimeout(Exception):
pass
test_timeout
,它将处理 with
块:class test_timeout:
def __init__(self, seconds, error_message=None):
if error_message is None:
error_message = 'test timed out after {}s.'.format(seconds)
self.seconds = seconds
self.error_message = error_message
def handle_timeout(self, signum, frame):
raise TestTimeout(self.error_message)
def __enter__(self):
signal.signal(signal.SIGALRM, self.handle_timeout)
signal.alarm(self.seconds)
def __exit__(self, exc_type, exc_val, exc_tb):
signal.alarm(0)
with test_timeout()
块:def test_foo(self):
with test_timeout(5): # test has 5 seconds to complete
... foo unit test code ...
使用这种方法,超时的测试将因
raise TestTimeout
异常而导致错误。
(可选)您可以将
with test_timeout()
块包装在 try: except TestTimeout:
块中,并以更细粒度处理异常(例如跳过测试而不是错误)。
使用线程的类似解决方案
import threading
class TimeoutError(Exception):
"""Custom exception raised when a timeout occurs."""
pass
class Timeout:
def __init__(self, seconds):
self.seconds = seconds
self._timer = None
self._timeout_occurred = False
def _timeout_handler(self):
self._timeout_occurred = True
raise TimeoutError(f"Operation exceeded the timeout of {self.seconds} seconds.")
def __enter__(self):
self._timeout_occurred = False
self._timer = threading.Timer(self.seconds, self._timeout_handler)
self._timer.start()
return self
def __exit__(self, exc_type, exc_value, traceback):
self._timer.cancel()
if exc_type is TimeoutError:
# Propagate the timeout exception
return False
# Suppress all other exceptions
return not self._timeout_occurred