如何使pytest等待(手动)用户操作?

时间:2017-03-13 09:18:40

标签: python pytest

我们成功地使用pytest(Python 3)来运行测试套件来测试一些硬件设备(电子设备)。 对于这些测试的子集,我们需要测试人员更改硬件配置,然后再将其更改。 我的方法是使用附加到相关测试的模块级夹具(它们都在一个单独的模块中),有两个input调用:

@pytest.fixture(scope="module")
def disconnect_component():
    input('Disconnect component, then press enter')
    yield  # At this point all the tests with this fixture are run
    input('Connect component again, then press enter')

运行时,我得到OSError: reading from stdin while output is captured。我可以通过使用--capture=no调用pytest来避免这种情况,并确认我的方法有效,这意味着我在问题的测试子集之前获得第一个查询,在运行之后获得第二个查询。

最大的缺点是,这会停止捕获整个测试套件的stdin / stderr,这是其他一些测试所依赖的。

我还试图像这样使用capsys.disableddocs

@pytest.fixture(scope="module")
def disconnect_component(capsys):
    with capsys.disabled():
        input('Disconnect component, then press enter')
        yield  # At this point all the tests with this fixture are run
        input('Connect component again, then press enter')

但在运行时,我得到ScopeMismatch: You tried to access the 'function' scoped fixture 'capsys' with a 'module' scoped request object, involved factories

我能否以input之外的其他方式让pytest等待用户操作?如果没有,我可以使用上面的夹具禁用捕获测试吗?

5 个答案:

答案 0 :(得分:6)

所以,我通过pytest dev找到了hint,基于此我基本上做了capsys.disable()函数所做的事情:

@pytest.fixture(scope="module")
def disconnect_component(pytestconfig):
    capmanager = pytestconfig.pluginmanager.getplugin('capturemanager')

    capmanager.suspend_global_capture(in_=True)
    input('Disconnect component, then press enter')
    capmanager.resume_global_capture()

    yield  # At this point all the tests with this fixture are run

    capmanager.suspend_global_capture(in_=True)
    input('Connect component again, then press enter')
    capmanager.resume_global_capture()

就我所见,这完美无瑕。不要忘记in_=True位。

编辑:从pytest 3.3.0(我认为),capmanager.suspendcapturecapmanager.resumecapture分别是renamedcapmanager.suspend_global_capturecapmanager.resume_global_capture

答案 1 :(得分:2)

也许值得注意的是,上述解决方案不一定要在灯具中。我为此做了一个辅助函数:

import pytest

def ask_user_input(msg=''):
    """ Asks user to check something manually and answer a question
    """
    notification = "\n\n???\tANSWER NEEDED\t???\n\n{}".format(msg)

    # suspend input capture by py.test so user input can be recorded here
    capture_manager = pytest.config.pluginmanager.getplugin('capturemanager')
    capture_manager.suspendcapture(in_=True)

    answer = raw_input(notification)

    # resume capture after question have been asked
    capture_manager.resumecapture()

    logging.debug("Answer: {}".format(answer))
    return answer

答案 2 :(得分:2)

如果您需要将inputpytest一起使用,以供将来参考。您可以在pytest的任何部分执行此操作,setup_classtest_...teardown_method等。这适用于pytest > 3.3.x

import pytest

capture_manager = pytest.config.pluginmanager.getplugin('capturemanager')
capture_manager.suspend_global_capture(in_=True)
answer = input('My reference text here')
capture_manager.resume_global_capture()

答案 3 :(得分:1)

从pytest 5开始,您可以使用以下工具:

@pytest.fixture
def suspend_capture(pytestconfig):
    class suspend_guard:
        def __init__(self):
            self.capmanager = pytestconfig.pluginmanager.getplugin('capturemanager')
        def __enter__(self):
            self.capmanager.suspend_global_capture(in_=True)
        def __exit__(self, _1, _2, _3):
            self.capmanager.resume_global_capture()

    yield suspend_guard()

用法示例:

def test_input(suspend_capture):
    with suspend_capture:
        input("hello")

答案 4 :(得分:0)

使用全局pytest.config对象的解决方案不再起作用。就我的用例而言,将--capture=sys与使用input()stdin的自定义stdout一起使用可以很好地工作。


def fd_input(prompt):
    with os.fdopen(os.dup(1), "w") as stdout:
        stdout.write("\n{}? ".format(prompt))

    with os.fdopen(os.dup(2), "r") as stdin:
        return stdin.readline()