¿Cómo imprimir en consola en pytest?

175

Estoy tratando de usar TDD (desarrollo basado en pruebas) con pytest. pytestNo printa la consola cuando lo uso print.

Estoy usando pytest my_tests.pypara ejecutarlo.

El documentationparece decir que debería funcionar por defecto: http://pytest.org/latest/capture.html

Pero:

import myapplication as tum

class TestBlogger:

    @classmethod
    def setup_class(self):
        self.user = "alice"
        self.b = tum.Blogger(self.user)
        print "This should be printed, but it won't be!"

    def test_inherit(self):
        assert issubclass(tum.Blogger, tum.Site)
        links = self.b.get_links(posts)
        print len(links)   # This won't print either.

No se imprime nada en mi consola de salida estándar (solo el progreso normal y cuántas pruebas pasaron / fallaron).

Y el script que estoy probando contiene print:

class Blogger(Site):
    get_links(self, posts):
        print len(posts)   # It won't get printed in the test.

En el unittestmódulo, todo se imprime por defecto, que es exactamente lo que necesito. Sin embargo, deseo utilizarlo pytestpor otros motivos.

¿Alguien sabe cómo hacer que se muestren las declaraciones impresas?

BBedit
fuente
1
Tal vez se está sobrescribiendo stdout. ¿Qué pasa si lo usas sys.stdout.write("Test")? ¿Qué tal sys.__stdout__.write("Test")? Este último siempre debe escribir en el stdout definido por el sistema, que debe ser la consola. Si los dos comandos hacen cosas diferentes, entonces se está cambiando stdout; si hacen lo mismo, entonces el problema es otra cosa.
TheSoundDefense

Respuestas:

205

De forma predeterminada, py.testcaptura el resultado de la salida estándar para que pueda controlar cómo se imprime. Si no hiciera esto, arrojaría mucho texto sin el contexto de qué prueba imprimió ese texto.

Sin embargo, si una prueba falla, incluirá una sección en el informe resultante que muestra lo que se imprimió según el estándar en esa prueba en particular.

Por ejemplo,

def test_good():
    for i in range(1000):
        print(i)

def test_bad():
    print('this should fail!')
    assert False

Resultados en la siguiente salida:

>>> py.test tmp.py
============================= test session starts ==============================
platform darwin -- Python 2.7.6 -- py-1.4.20 -- pytest-2.5.2
plugins: cache, cov, pep8, xdist
collected 2 items

tmp.py .F

=================================== FAILURES ===================================
___________________________________ test_bad ___________________________________

    def test_bad():
        print('this should fail!')
>       assert False
E       assert False

tmp.py:7: AssertionError
------------------------------- Captured stdout --------------------------------
this should fail!
====================== 1 failed, 1 passed in 0.04 seconds ======================

Tenga en cuenta la Captured stdoutsección.

Si desea ver las printdeclaraciones a medida que se ejecutan, puede pasar el -sindicador a py.test. Sin embargo, tenga en cuenta que esto a veces puede ser difícil de analizar.

>>> py.test tmp.py -s
============================= test session starts ==============================
platform darwin -- Python 2.7.6 -- py-1.4.20 -- pytest-2.5.2
plugins: cache, cov, pep8, xdist
collected 2 items

tmp.py 0
1
2
3
... and so on ...
997
998
999
.this should fail!
F

=================================== FAILURES ===================================
___________________________________ test_bad ___________________________________

    def test_bad():
        print('this should fail!')
>       assert False
E       assert False

tmp.py:7: AssertionError
====================== 1 failed, 1 passed in 0.02 seconds ======================
tbekolay
fuente
2
Eminentemente práctico. ¡Gran trabajo!
cmc
1
hmm ... todavía no registra mis declaraciones impresas
Tim Boland
68

El uso de la -sopción imprimirá la salida de todas las funciones, lo que puede ser demasiado.

Si necesita un resultado particular, la página de documentos que mencionó ofrece algunas sugerencias:

  1. Inserte assert False, "dumb assert to make PyTest print my stuff"al final de su función, y verá su salida debido a una prueba fallida.

  2. PyTest le ha pasado un objeto especial y puede escribir el resultado en un archivo para inspeccionarlo más tarde, como

    def test_good1(capsys):
        for i in range(5):
            print i
        out, err = capsys.readouterr()
        open("err.txt", "w").write(err)
        open("out.txt", "w").write(out)

    Puede abrir los archivos outy erren una pestaña separada y dejar que el editor lo actualice automáticamente, o hacer un simple py.test; cat out.txtcomando de shell para ejecutar su prueba.

Esa es una forma bastante complicada de hacer cosas, pero puede ser que sea lo que necesita: después de todo, TDD significa que se mete con cosas y lo deja limpio y silencioso cuando está listo :-).

dmitry_romanov
fuente
probé la versión 1. con pytest 3.8.1 desafortunadamente solo imprime el bloque de función de prueba, pero no el resultado de las declaraciones de impresión :( ¿hay más trucos para esto?
UV
@UV: en lugar de utilizar la print()función, debe colocar la variable o el mensaje que desea imprimir después de la coma en la declaración de aserción. Por ejemplo assert False, what_are_you, "imprimirá" el valor de what_are_youen el informe de pytest.
Mart Van de Ven
43

Respuesta corta

Usa la -sopción:

pytest -s

Respuesta detallada

De los documentos :

Durante la ejecución de la prueba, se captura cualquier salida enviada a stdout y stderr . Si una prueba o un método de configuración falla, su salida capturada correspondiente generalmente se mostrará junto con el rastreo de fallas.

pytesttiene la opción --capture=methoden la que methodes per-test captura de método, y podría ser una de las siguientes: fd, syso no. pytesttambién tiene la opción -sque es un acceso directo para --capture=no, y esta es la opción que le permitirá ver sus declaraciones de impresión en la consola.

pytest --capture=no     # show print statements in console
pytest -s               # equivalent to previous command

Establecer métodos de captura o deshabilitar la captura

Hay dos formas de pytestrealizar la captura:

  1. captura de nivel del descriptor de archivo (FD) (predeterminado): se capturarán todas las escrituras que se dirijan a los descriptores de archivo 1 y 2 del sistema operativo.

  2. captura de nivel sys : solo se capturarán las escrituras en archivos Python sys.stdout y sys.stderr. No se realiza la captura de escrituras en descriptores de archivos.

pytest -s            # disable all capturing
pytest --capture=sys # replace sys.stdout/stderr with in-mem files
pytest --capture=fd  # also point filedescriptors 1 and 2 to temp file
lmiguelvargasf
fuente
17

Necesitaba imprimir una advertencia importante sobre las pruebas omitidas exactamente cuando estaba PyTestsilenciado, literalmente, todo .

No quería fallar una prueba para enviar una señal, así que hice un truco de la siguiente manera:

def test_2_YellAboutBrokenAndMutedTests():
    import atexit
    def report():
        print C_patch.tidy_text("""
In silent mode PyTest breaks low level stream structure I work with, so
I cannot test if my functionality work fine. I skipped corresponding tests.
Run `py.test -s` to make sure everything is tested.""")
    if sys.stdout != sys.__stdout__:
        atexit.register(report)

El atexitmódulo me permite imprimir cosas después de PyTest lanzar las secuencias de salida. El resultado es el siguiente:

============================= test session starts ==============================
platform linux2 -- Python 2.7.3, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: /media/Storage/henaro/smyth/Alchemist2-git/sources/C_patch, inifile: 
collected 15 items 

test_C_patch.py .....ssss....s.

===================== 10 passed, 5 skipped in 0.15 seconds =====================
In silent mode PyTest breaks low level stream structure I work with, so
I cannot test if my functionality work fine. I skipped corresponding tests.
Run `py.test -s` to make sure everything is tested.
~/.../sources/C_patch$

El mensaje se imprime incluso cuando PyTestestá en modo silencioso, y no se imprime si ejecuta cosas py.test -s, por lo que todo ya está bien probado.

dmitry_romanov
fuente
1
Perfecto para generar métricas de prueba personalizadas.
z0r
5

Según los documentos de pytest , pytest --capture=sysdebería funcionar. Si desea capturar el estándar dentro de una prueba, consulte el accesorio capsys.

Karthik Gomadam Rajagopal
fuente
Es trabajo para mí cuando necesito una variable de impresión en la terminal ...
Sukma Saputra
2

Originalmente vine aquí para encontrar cómo PyTestimprimir en la consola de VSCode mientras ejecutaba / depuraba la prueba de la unidad desde allí. Esto se puede hacer con la siguiente launch.jsonconfiguración. Dada .venvla carpeta del entorno virtual.

    "version": "0.2.0",
    "configurations": [
        {
            "name": "PyTest",
            "type": "python",
            "request": "launch",
            "stopOnEntry": false,
            "pythonPath": "${config:python.pythonPath}",
            "module": "pytest",
            "args": [
                "-sv"
            ],
            "cwd": "${workspaceRoot}",
            "env": {},
            "envFile": "${workspaceRoot}/.venv",
            "debugOptions": [
                "WaitOnAbnormalExit",
                "WaitOnNormalExit",
                "RedirectOutput"
            ]
        }
    ]
}
dummyDev
fuente