gpt4 book ai didi

python - pytest-timeout - 测试失败而不是杀死整个测试运行

转载 作者:太空宇宙 更新时间:2023-11-04 02:40:13 43 4
gpt4 key购买 nike

我知道在 pytest-timeout 中我可以为每个测试用例指定超时,但单一失败会终止整个测试运行,而不是使松弛测试用例失败。

我是被迫为此制定自己的解决方案,还是有提供该解决方案的现成可用工具?

最佳答案

我很久以前就研究过这个问题,也得出结论,自制的解决方案会更好。

我的插件正在终止整个 pytest 进程,但可以对其进行调整,使其仅使单个(当前)测试容易失败。这是调整后的草稿:

import pytest
import signal


class Termination(SystemExit):
pass


class TimeoutExit(BaseException):
pass


def _terminate(signum, frame):
raise Termination("Runner is terminated from outside.")


def _timeout(signum, frame):
raise TimeoutExit("Runner timeout is reached, runner is terminating.")


@pytest.hookimpl
def pytest_addoption(parser):
parser.addoption(
'--timeout', action='store', dest='timeout', type=int, default=None,
help="number of seconds before each test failure")


@pytest.hookimpl
def pytest_configure(config):
# Install the signal handlers that we want to process.
signal.signal(signal.SIGTERM, _terminate)
signal.signal(signal.SIGALRM, _timeout)


@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_protocol(item, nextitem):

# Set the per-test timeout (an alarm signal).
if item.config.option.timeout is not None:
signal.alarm(item.config.option.timeout)

try:
# Run the setup, test body, and teardown stages.
yield
finally:
# Disable the alarm when the test passes or fails.
# I.e. when we get into the framework's body.
signal.alarm(0)

当你执行kill -ALRM $pid时,或者当每个测试由于预设警报而单独超时时,只有当前测试会失败,但其他测试会继续。

并且这个TimeoutExit 不会被执行except Exception: pass 的库抑制,因为它继承自BaseException

因此,它在这方面类似于SystemExit。但是,与 SystemExitKeyboardInterruption 不同,pytest 不会捕获它,也不会在出现此类异常时退出。

异常将被注入(inject)到警报发生时测试执行的任何地方,即使它执行了 time.sleep(...)(对于任何信号)也是如此。

请记住,您只能为进程设置一个警报(操作系统限制)。这也使得它与 pytest-timeout 不兼容,因为它也出于相同目的使用 ALRM 信号。

如果你想要全局和每个测试超时,你必须实现你的智能警报管理器,它会跟踪一些警报,将操作系统警报设置为最早的警报,并决定调用哪个处理程序收到报警信号。


以防万一,当你执行 kill -TERM $pid 或只是 kill $pid(优雅终止)时,它会立即终止——因为它继承自 SystemExit,这是 BaseException,通常不会被代码或 pytest 捕获。

后一种情况主要展示了如何对不同的信号设置不同的 react 。您可以对 USR1 和 USR2 以及其他可捕捉的信号执行类似的操作。


为了快速测试,将上面的插件代码放入 conftest.py 文件(一个伪插件)。

考虑这个测试文件:

import time

def test_this():
try:
time.sleep(10)
except Exception:
pass

def test_that():
pass

在没有超时的情况下运行 pytest 没有任何作用,并且两个测试都通过了:

$ pytest -s -v
.........
collected 2 items

test_me.py::test_this PASSED
test_me.py::test_that PASSED

======= 2 passed in 10.02 seconds =======

在超时的情况下运行它会在第一个测试中失败,但会通过第二个测试:

$ pytest -s -v --timeout=5
.........
collected 2 items

test_me.py::test_this FAILED
test_me.py::test_that PASSED

============== FAILURES ==============
______________ test_this _____________

def test_this():
try:
> time.sleep(10)

test_me.py:5:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

signum = 14, frame = <frame object at 0x106b3c428>

def _timeout(signum, frame):
> raise TimeoutExit("Runner timeout is reached, runner is terminating.")
E conftest.pytest_configure.<locals>.TimeoutExit: Runner timeout is reached, runner is terminating.

conftest.py:24: TimeoutExit
======= 1 failed, 1 passed in 5.11 seconds =======

关于python - pytest-timeout - 测试失败而不是杀死整个测试运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46766899/

43 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com