2017-10-16 559 views

回答

1

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

我的插件正在杀死整个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不会捕获它,并且不会退出这样的异常。

即使在time.sleep(...)(对于任何信号)的情况下,异常都会被注入到测试所发生的任何地方。

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

如果您希望全局测试超时时间为&,则必须实现智能报警管理器,该管理器将跟踪少量报警,将操作系统报警设置为最早的报警,并决定在何时调用哪个处理程序接收到报警信号。


在情况下,当你做kill -TERM $pid或只是kill $pid(正常终止)时,它会立即终止 - 因为它是从SystemExit,这是BaseException,通常不是由代码或pytest抓住继承。

后一种情况主要演示如何对不同的信号设置不同的反应。您可以使用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 ======= 
+0

如果我有时间我不认为我会深入实施的细节深入挖掘,但这个我真的很有启发!我会保留这些作为参考。 – JCode

相关问题