unittest unit test run example python unit-testing nosetests python-nose

run - ¿Cómo afirmar el resultado con nosetest/unittest en python?



unit test en python (9)

Estoy escribiendo pruebas para una función como la siguiente:

def foo(): print ''hello world!''

Entonces, cuando quiera probar esta función, el código será así:

import sys from foomodule import foo def test_foo(): foo() output = sys.stdout.getline().strip() # because stdout is an StringIO instance assert output == ''hello world!''

Pero si ejecuto nosetests con el parámetro -s, la prueba se bloquea. ¿Cómo puedo capturar la salida con unittest o módulo de nariz?


Basado en la respuesta de Rob Kennedy, escribí una versión basada en clases del administrador de contexto para almacenar la salida.

El uso es como:

with OutputBuffer() as bf: print(''hello world'') assert bf.out == ''hello world/n''

Aquí está la implementación:

from io import StringIO import sys class OutputBuffer(object): def __init__(self): self.stdout = StringIO() self.stderr = StringIO() def __enter__(self): self.original_stdout, self.original_stderr = sys.stdout, sys.stderr sys.stdout, sys.stderr = self.stdout, self.stderr return self def __exit__(self, exception_type, exception, traceback): sys.stdout, sys.stderr = self.original_stdout, self.original_stderr @property def out(self): return self.stdout.getvalue() @property def err(self): return self.stderr.getvalue()


Desde la versión 2.7, ya no es necesario reasignar sys.stdout , esto se proporciona a través del indicador de buffer . Además, es el comportamiento predeterminado de nosetest.

Aquí hay un ejemplo de error en el contexto no almacenado en el búfer:

import sys import unittest def foo(): print ''hello world!'' class Case(unittest.TestCase): def test_foo(self): foo() if not hasattr(sys.stdout, "getvalue"): self.fail("need to run in buffered mode") output = sys.stdout.getvalue().strip() # because stdout is an StringIO instance self.assertEquals(output,''hello world!'')

Puede configurar el búfer a través de la línea de comando de la --buffer , indicador -b , --buffer o en unittest.main opciones. Lo opuesto se logra a través de la bandera de --nocapture .

if __name__=="__main__": assert not hasattr(sys.stdout, "getvalue") unittest.main(module=__name__, buffer=True, exit=False) #. #---------------------------------------------------------------------- #Ran 1 test in 0.000s # #OK assert not hasattr(sys.stdout, "getvalue") unittest.main(module=__name__, buffer=False) #hello world! #F #====================================================================== #FAIL: test_foo (__main__.Case) #---------------------------------------------------------------------- #Traceback (most recent call last): # File "test_stdout.py", line 15, in test_foo # self.fail("need to run in buffered mode") #AssertionError: need to run in buffered mode # #---------------------------------------------------------------------- #Ran 1 test in 0.002s # #FAILED (failures=1)


En Python 3.5 puede usar contextlib.redirect_stdout() y StringIO() . Aquí está la modificación de tu código

import contextlib from io import StringIO from foomodule import foo def test_foo(): temp_stdout = StringIO() with contextlib.redirect_stdout(temp_stdout): foo() output = temp_stdout.getvalue().strip() assert output == ''hello world!''


Las pruebas de escritura a menudo nos muestran una mejor manera de escribir nuestro código. Al igual que la respuesta de Shane, me gustaría sugerir otra forma de ver esto. ¿Realmente desea afirmar que su programa generó una determinada cadena, o simplemente que construyó una determinada cadena para la salida? Esto se vuelve más fácil de probar, ya que probablemente podamos suponer que la declaración de print Python hace su trabajo correctamente.

def foo_msg(): return ''hello world'' def foo(): print foo_msg()

Entonces tu prueba es muy simple:

def test_foo_msg(): assert ''hello world'' == foo_msg()

Por supuesto, si realmente necesita probar el rendimiento real de su programa, no dude en ignorarlo. :)


Muchas de estas respuestas me fallaron porque no puedes from StringIO import StringIO en Python 3. Aquí hay un fragmento de trabajo mínimo basado en el comentario de @ naxa y en el Python Cookbook.

from io import StringIO from unittest.mock import patch with patch(''sys.stdout'', new=StringIO()) as fakeOutput: print(''hello world'') self.assertEqual(fakeOutput.getvalue().strip(), ''hello world'')


O considere usar pytest , tiene soporte incorporado para afirmar stdout y stderr. Ver docs

def test_myoutput(capsys): # or use "capfd" for fd-level print("hello") captured = capsys.readouterr() assert captured.out == "hello/n" print("next") captured = capsys.readouterr() assert captured.out == "next/n"


Recién estoy aprendiendo Python y me encontré luchando con un problema similar al anterior con pruebas unitarias de métodos con salida. Mi prueba de unidad de paso para el módulo foo anterior ha terminado pareciéndose a esto:

import sys import unittest from foo import foo from StringIO import StringIO class FooTest (unittest.TestCase): def setUp(self): self.held, sys.stdout = sys.stdout, StringIO() def test_foo(self): foo() self.assertEqual(sys.stdout.getvalue(),''hello world!/n'')


Si realmente desea hacer esto, puede reasignar sys.stdout durante la prueba.

def test_foo(): import sys from foomodule import foo from StringIO import StringIO saved_stdout = sys.stdout try: out = StringIO() sys.stdout = out foo() output = out.getvalue().strip() assert output == ''hello world!'' finally: sys.stdout = saved_stdout

Sin embargo, si estuviera escribiendo este código, preferiría pasar un parámetro de out opcional a la función foo .

def foo(out=sys.stdout): out.write("hello, world!")

Entonces la prueba es mucho más simple:

def test_foo(): from foomodule import foo from StringIO import StringIO out = StringIO() foo(out=out) output = out.getvalue().strip() assert output == ''hello world!''


Uso este administrador de contexto para capturar resultados. En última instancia, utiliza la misma técnica que algunas de las otras respuestas mediante el reemplazo temporal de sys.stdout . Prefiero el administrador de contexto porque envuelve toda la contabilidad en una sola función, por lo que no tengo que volver a escribir ningún código try-finally, y no tengo que escribir funciones de configuración y eliminación solo para esto.

import sys from contextlib import contextmanager from StringIO import StringIO @contextmanager def captured_output(): new_out, new_err = StringIO(), StringIO() old_out, old_err = sys.stdout, sys.stderr try: sys.stdout, sys.stderr = new_out, new_err yield sys.stdout, sys.stderr finally: sys.stdout, sys.stderr = old_out, old_err

Úselo así:

with captured_output() as (out, err): foo() # This can go inside or outside the `with` block output = out.getvalue().strip() self.assertEqual(output, ''hello world!'')

Además, dado que el estado de salida original se restablece al salir del bloque with , podemos configurar un segundo bloque de captura en la misma función que el primero, lo que no es posible con las funciones de configuración y desmontaje, y se vuelve prolijo cuando se escribe try- finalmente bloquea manualmente. Esa capacidad fue útil cuando el objetivo de una prueba era comparar los resultados de dos funciones entre sí en lugar de un valor precalculado.