我们成功地使用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.disabled
(docs)
@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等待用户操作?如果没有,我可以使用上面的夹具禁用捕获测试吗?
答案 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.suspendcapture
和capmanager.resumecapture
分别是renamed到capmanager.suspend_global_capture
和capmanager.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)
如果您需要将input
与pytest
一起使用,以供将来参考。您可以在pytest的任何部分执行此操作,setup_class
,test_...
,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()