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

Jur*_*ury 19 python unit-testing timeout

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

Lav*_*Lav 22

据我所知unittest,不包含对测试超时的任何支持.

您可以timeout-decorator从PyPI 尝试库.在各个测试中应用装饰器,如果它们花费太长时间使它们终止:

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 ...
Run Code Online (Sandbox Code Playgroud)

要创建全局超时,您可以替换呼叫

unittest.main()
Run Code Online (Sandbox Code Playgroud)

timeout_decorator.timeout(GLOBAL_TIMEOUT)(unittest.main)()
Run Code Online (Sandbox Code Playgroud)


caf*_*eyd 8

根据这个答案unittest使用上下文管理器(withkeyowrd)构建了一个超时解决方案。

这种方法也使用signal,所以它可能只在 *nix 系统上有效(我只在我的 Ubuntu 16.04 环境中运行它)。

  1. 导入信号,添加TestTimeout异常:
import signal

...

class TestTimeout(Exception):
    pass
Run Code Online (Sandbox Code Playgroud)
  1. 定义 class 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)
Run Code Online (Sandbox Code Playgroud)
  1. with test_timeout()在单元测试中嵌入块:
def test_foo(self):
  with test_timeout(5):  # test has 5 seconds to complete
    ... foo unit test code ...
Run Code Online (Sandbox Code Playgroud)

使用这种方法,超时的测试将由于异常而导致错误raise TestTimeout

或者,您可以将with test_timeout()块包装在一个try: except TestTimeout:块中,并以更大的粒度处理异常(例如跳过测试而不是错误)。