如何为 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
被_Timeout
的self
丢失(替换)。调用时,testmethod
没有任何 self
并且它失败了。我不知道发生了什么以及如何解决它。全局超时的技巧也不起作用。
@Jury 我确实让它起作用了。只能使用装饰器,但有例外:@timeout_decorator.timeout(TIMEOUT, timeout_exception=StopIteration)
。您需要将此装饰器放在所有潜在的堆叠测试中。就我而言,它是在与 async/websocket 相关的测试中
@AdamWildavsky 恐怕 C++ 代码中会发生太多事情。你可以尝试在一个子进程中运行你的整个测试套件,父进程负责超时——这有点像大锤方法,但这是我能想到的最好的方法。或许将其作为一个单独的问题更好,因为它应该被视为这样的恕我直言。以上是关于如何为 python unittest 指定测试超时?的主要内容,如果未能解决你的问题,请参考以下文章
如何为 Python socketserver 请求处理程序编写单元测试?