Estamos utilizando con éxito pytest (Python 3) para ejecutar un conjunto de pruebas que prueba algunos dispositivos de hardware (electrónicos). Para un subconjunto de estas pruebas, necesitamos que el probador cambie la disposición del hardware y luego la vuelva a cambiar. Mi enfoque fue usar un accesorio de nivel de módulo adjunto a las pruebas en cuestión (que están todas en un módulo separado), con dos llamadas de 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') Cuando ejecuto esto, obtengo OSError: reading from stdin while output is captured . Puedo evitar esto llamando a pytest con --capture=no , y confirmé que mi enfoque funciona, lo que significa que obtengo la primera consulta antes del subconjunto de prueba en cuestión y la segunda después de que se hayan ejecutado.
El gran inconveniente es que esto desactiva la captura de stdin/stderr para todo el conjunto de pruebas, en el que se basan algunas de las otras pruebas.
También traté de usar capsys.disabled ( docs ) así
@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') pero cuando ejecuto esto, obtengo ScopeMismatch: You tried to access the 'function' scoped fixture 'capsys' with a 'module' scoped request object, involved factories .
¿Puedo hacer que pytest espere la acción del usuario de otra manera que no sea input ? Si no, ¿puedo deshabilitar la captura solo para las pruebas usando el dispositivo anterior?
Entonces, encontré una pista de un desarrollador de pytest, en base a la cual básicamente hago lo que hace la función 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() Esto funciona perfectamente por lo que puedo ver. No olvide el in_=True .
Editar: desde pytest 3.3.0 (creo), capmanager.suspendcapture y capmanager.resumecapture cambiaron de nombre a capmanager.suspend_global_capture y capmanager.resume_global_capture , respectivamente.
A partir de pytest 5, como accesorio, puede usar esto:
@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()Ejemplo de uso:
def test_input(suspend_capture): with suspend_capture: input("hello")Tal vez valga la pena señalar que la solución anterior no tiene que estar en un accesorio. He hecho una función de ayuda para eso:
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