¿Cómo puedo simular la entrada a stdin para pyunit?

Estoy tratando de probar una función que toma entrada de stdin , que actualmente estoy probando con algo como esto:

 cat /usr/share/dict/words | ./spellchecker.py 

En el nombre de la automatización de prueba, ¿hay alguna manera en que pyunit pueda falsificar la entrada a raw_input() ?

La respuesta corta es mono patch raw_input() .

Hay algunos buenos ejemplos en la respuesta a ¿Cómo mostrar la entrada estándar redirigida en Python?

Este es un ejemplo simple y trivial que utiliza un lambda que desecha el aviso y devuelve lo que queremos.

Sistema bajo prueba

 cat ./name_getter.py #!/usr/bin/env python class NameGetter(object): def get_name(self): self.name = raw_input('What is your name? ') def greet(self): print 'Hello, ', self.name, '!' def run(self): self.get_name() self.greet() if __name__ == '__main__': ng = NameGetter() ng.run() $ echo Derek | ./name_getter.py What is your name? Hello, Derek ! 

Caso de prueba:

 $ cat ./t_name_getter.py #!/usr/bin/env python import unittest import name_getter class TestNameGetter(unittest.TestCase): def test_get_alice(self): name_getter.raw_input = lambda _: 'Alice' ng = name_getter.NameGetter() ng.get_name() self.assertEquals(ng.name, 'Alice') def test_get_bob(self): name_getter.raw_input = lambda _: 'Bob' ng = name_getter.NameGetter() ng.get_name() self.assertEquals(ng.name, 'Bob') if __name__ == '__main__': unittest.main() $ ./t_name_getter.py -v test_get_alice (__main__.TestNameGetter) ... ok test_get_bob (__main__.TestNameGetter) ... ok ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK 

Actualización – utilizando unittest.mock.patch

Desde Python 3.3, hay un nuevo submódulo para unittest llamado mock que hace exactamente lo que necesita hacer. Para aquellos que usan Python 2.6 o superior hay un backport de mock encuentra aquí .

 import unittest from unittest.mock import patch import module_under_test class MyTestCase(unittest.TestCase): def setUp(self): # raw_input is untouched before test assert module_under_test.raw_input is __builtins__.raw_input def test_using_with(self): input_data = "123" expected = int(input_data) with patch.object(module_under_test, "raw_input", create=True, return_value=expected): # create=True is needed as raw_input is not in the globals of # module_under_test, but actually found in __builtins__ . actual = module_under_test.function() self.assertEqual(expected, actual) @patch.object(module_under_test, "raw_input", create=True) def test_using_decorator(self, raw_input): raw_input.return_value = input_data = "123" expected = int(input_data) actual = module_under_test.function() self.assertEqual(expected, actual) def tearDown(self): # raw input is restred after test assert module_under_test.raw_input is __builtins__.raw_input if __name__ == "__main__": unittest.main() # where module_under_test.function is: def function(): return int(raw_input("prompt> ")) 

Respuesta anterior – reemplazando sys.stdin

Creo que el módulo sys podría ser lo que estás buscando.

Puedes hacer algo como

 import sys # save actual stdin in case we need it again later stdin = sys.stdin sys.stdin = open('simulatedInput.txt','r') # or whatever else you want to provide the input eg. StringIO 

raw_input ahora leerá desde simulatedInput.txt cada vez que se llame. Si el contenido de SimulatedInput fuera

 hello bob 

luego la primera llamada a raw_input devolvería “hola”, la segunda “bob” y la tercera lanzaría un EOFError ya que no había más texto para leer.

Reemplace sys.stdin con una instancia de StringIO y cargue la instancia de StringIO con los datos que desea que se devuelvan a través de sys.stdin . Además, sys.__stdin__ contiene el objeto sys.stdin original, por lo que restaurar sys.stdin después de su prueba es tan simple como sys.stdin = sys.__stdin__ .

Fudge es un excelente módulo simulado de python, con decoradores convenientes para hacer parches como este para ti, con limpieza automática. Usted deberia comprobar esto.

No describiste qué tipo de código está en spellchecker.py , lo que me da libertad para especular.

Supongamos que es algo como esto:

 import sys def check_stdin(): # some code that uses sys.stdin 

Para mejorar la check_stdin de check_stdin función check_stdin , propongo refactorizarlo así:

 def check_stdin(): return check(sys.stdin) def check(input_stream): # same as original code, but instead of # sys.stdin it is written it terms of input_stream. 

Ahora, la mayor parte de su lógica está en la función de check , y puede elaborar manualmente cualquier entrada que pueda imaginar para probarla correctamente, sin necesidad de lidiar con la stdin .

Mis 2 centavos.

Si está utilizando un módulo simulado (escrito por Michael Foord), para simular la función raw_input puede usar una syntax como:

 @patch('src.main.raw_input', create=True, new=MagicMock(return_value='y')) def test_1(self): method_we_try_to_test(); # method or function that calls **raw_input**