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

Posted

技术标签:

【中文标题】如何为 python unittest 指定测试超时?【英文标题】:How to specify test timeout for python unittest? 【发布时间】:2016-04-17 01:19:53 【问题描述】:

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

【问题讨论】:

【参考方案1】:

我使用上下文管理器(with keyowrd)、based on this answer 构建了一个 unittest 超时解决方案。

这种方法也使用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: 块中,并以更精细的方式处理异常(例如,跳过测试而不是错误)。

【讨论】:

至少对于 2.7(可能适用于 3.x)- pip install timeout_wrapper - 正是这样做的模块 - 使用 signal.signal 和 signal.SIGALRM - 所以这个模块就是这个答案。来自已接受答案的超时装饰器使用相同但更新鲜和更多的功能【参考方案2】:

据我所知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 ...

要创建全局超时,可以替换调用

unittest.main()

timeout_decorator.timeout(GLOBAL_TIMEOUT)(unittest.main)()

【讨论】:

有趣。我没有使用unittest.main(),但我希望我的情况可以采用decorator。但是我的测试不是在单线程中进行的...... @Jury 检查timeout-decorator reference 中的“多线程”部分 - 您只需要在多线程环境中使用timeout_decorator.timeout(TIMEOUT, use_signals=False) 我不知道发生了什么事,但use_signals=False 对我不起作用,但它看起来工作的信号。正如我发现的那样,这个模块使钩子直接调用_Timeout.__call__ 而不是 testmethod。问题实际上是 testmethod 的self_Timeoutself 丢失(替换)。调用时,testmethod 没有任何 self 并且它失败了。我不知道发生了什么以及如何解决它。全局超时的技巧也不起作用。 @Jury 我确实让它起作用了。只能使用装饰器,但有例外:@timeout_decorator.timeout(TIMEOUT, timeout_exception=StopIteration)。您需要将此装饰器放在所有潜在的堆叠测试中。就我而言,它是在与 async/websocket 相关的测试中 @AdamWildavsky 恐怕 C++ 代码中会发生太多事情。你可以尝试在一个子进程中运行你的整个测试套件,父进程负责超时——这有点像大锤方法,但这是我能想到的最好的方法。或许将其作为一个单独的问题更好,因为它应该被视为这样的恕我直言。

以上是关于如何为 python unittest 指定测试超时?的主要内容,如果未能解决你的问题,请参考以下文章

如何为 Python socketserver 请求处理程序编写单元测试?

Python+Selenium+Unittest编写超链接点击测试用例

如何为 angular2 的登录组件编写测试(单元测试)

超详细unittest单元测试框架总结

如何为 mocha 指定测试目录?

QT Creator:如何为自定义小部件生成库和测试可执行文件