如何为python unittest指定测试超时?

问题描述 投票:0回答:3

我正在使用

python
框架
unittest
。是否可以通过框架的能力指定测试超时?如果不是,是否可以为所有测试优雅地指定
timeout
并为某些单独的测试指定每个测试的私有值?
我想为所有测试定义一个
global timeout
(默认情况下他们将使用它),并为某些可能需要很长时间的测试定义超时。

python unit-testing timeout
3个回答
35
投票

据我所知

unittest
不包含任何对测试超时的支持。

您可以尝试 PyPI 中的

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)()

16
投票

我使用上下文管理器(

unittest
关键字)构建了一个
with
超时解决方案,基于这个答案

这种方法也使用了

signal
,所以它可能只在 *nix 系统上有效(我只在我的 Ubuntu 16.04 环境中运行过它)。

  1. 导入信号,添加
    TestTimeout
    例外:
import signal

...

class TestTimeout(Exception):
    pass
  1. 定义类
    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)
  1. 在单元测试中嵌入
    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:
块中,并以更细粒度处理异常(例如跳过测试而不是错误)。


0
投票

使用线程的类似解决方案

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
© www.soinside.com 2019 - 2024. All rights reserved.