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

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

【问题讨论】:

    标签: python unit-testing timeout


    【解决方案1】:

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

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

    【讨论】:

    • 至少对于 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++ 代码中会发生太多事情。你可以尝试在一个子进程中运行你的整个测试套件,父进程负责超时——这有点像大锤方法,但这是我能想到的最好的方法。或许将其作为一个单独的问题更好,因为它应该被视为这样的恕我直言。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多