Comment puis-je voir la sortie d'impression normale créée pendant l'exécution de Pytest?

401

Parfois, je veux simplement insérer des instructions d'impression dans mon code et voir ce qui est imprimé lorsque je l'exerce. Ma façon habituelle de "faire de l'exercice" est avec les tests de pytest existants. Mais lorsque je les exécute, je ne semble pas voir de sortie standard (au moins depuis PyCharm, mon IDE).

Existe-t-il un moyen simple de voir la sortie standard lors d'un test Pytest?

Des
la source
Seulement en cas d'échec ou toujours?
17
-s désactive la capture par test
hpk42
3
@delnan - J'aimerais toujours le voir
Des

Réponses:

51

Dans un commentaire en faveur de la réponse acceptée , Joe demande:

Existe-t-il un moyen d'imprimer sur la console ET de capturer la sortie afin qu'elle s'affiche dans le rapport junit?

Sous UNIX, cela est communément appelé départ . Idéalement, le tee plutôt que la capture serait la valeur par défaut de py.test. Dans l'idéal, ni py.test ni aucun autre plugin py.test tiers existant (... que je sache, de toute façon ) ne prend en charge le tee - malgré le fait que Python supporte trivialement le tee out-of-the-box .

Le py.test de correction de singe pour faire tout ce qui n'est pas pris en charge n'est pas anodin. Pourquoi? Parce que:

  • La plupart des fonctionnalités de py.test sont verrouillées derrière un _pytestpackage privé non destiné à être importé en externe. Si vous tentez de le faire sans savoir ce que vous faites, le pytestpackage public déclenche généralement des exceptions obscures lors de l'exécution. Merci beaucoup, py.test. Une architecture vraiment robuste que vous avez obtenue.
  • Même lorsque vous savez comment corriger de _pytestmanière sécurisée l'API privée de manière simple , vous devez le faire avant d' exécuter le pytestpackage public exécuté par la py.testcommande externe . Vous ne pouvez pas le faire dans un plugin (par exemple, un conftestmodule de niveau supérieur dans votre suite de tests). Au moment où py.test parvient paresseusement à importer dynamiquement votre plug-in, toute classe py.test que vous vouliez faire un patch de singe a depuis longtemps été instanciée - et vous n'avez pas accès à cette instance. Cela implique que, si vous souhaitez que votre patch de singe soit appliqué de manière significative, vous ne pouvez plus exécuter la py.testcommande externe en toute sécurité . Au lieu de cela, vous devez envelopper l'exécution de cette commande avec un setuptools personnalisétest commande que (dans l'ordre):
    1. Monkey patche l' _pytestAPI privée .
    2. Appelle la pytest.main()fonction publique pour exécuter la py.testcommande.

Cette réponse monkey-patches py.test -set les --capture=nooptions pour capturer stderr mais pas stdout. Par défaut, ces options ne capturent ni stderr ni stdout. Ce n'est pas tout à fait évident, bien sûr. Mais chaque grand voyage commence par une préquelle fastidieuse que tout le monde oublie dans cinq ans.

Pourquoi faire ceci? Je vais maintenant vous le dire. Ma suite de tests pilotée par py.test contient des tests fonctionnels lents. L'affichage de la sortie standard de ces tests est utile et rassurant, empêchant leycec d'atteindre killall -9 py.testquand encore un autre test fonctionnel de longue durée ne fait rien pendant des semaines. Cependant, l'affichage du stderr de ces tests empêche py.test de signaler les retraits d'exception en cas d'échecs de test. Ce qui est complètement inutile. Par conséquent, nous contraignons py.test à capturer stderr mais pas stdout.

Avant d'y arriver, cette réponse suppose que vous disposez déjà d'une testcommande setuptools personnalisée appelant py.test. Si ce n'est pas le cas, consultez la sous-section Intégration manuelle de la page bien écrite des bonnes pratiques de py.test .

Ne pas installer pytest-runner , un setuptools tiers fournissant un plugin d' setuptools personnalisés testcommande demandant également py.test. Si pytest-runner est déjà installé, vous devrez probablement désinstaller ce package pip3, puis adopter l'approche manuelle liée à ci-dessus.

En supposant que vous ayez suivi les instructions de l' intégration manuelle soulignées ci-dessus, votre base de code devrait maintenant contenir une PyTest.run_tests()méthode. Modifiez cette méthode pour qu'elle ressemble à:

class PyTest(TestCommand):
             .
             .
             .
    def run_tests(self):
        # Import the public "pytest" package *BEFORE* the private "_pytest"
        # package. While importation order is typically ignorable, imports can
        # technically have side effects. Tragicomically, that is the case here.
        # Importing the public "pytest" package establishes runtime
        # configuration required by submodules of the private "_pytest" package.
        # The former *MUST* always be imported before the latter. Failing to do
        # so raises obtuse exceptions at runtime... which is bad.
        import pytest
        from _pytest.capture import CaptureManager, FDCapture, MultiCapture

        # If the private method to be monkey-patched no longer exists, py.test
        # is either broken or unsupported. In either case, raise an exception.
        if not hasattr(CaptureManager, '_getcapture'):
            from distutils.errors import DistutilsClassError
            raise DistutilsClassError(
                'Class "pytest.capture.CaptureManager" method _getcapture() '
                'not found. The current version of py.test is either '
                'broken (unlikely) or unsupported (likely).'
            )

        # Old method to be monkey-patched.
        _getcapture_old = CaptureManager._getcapture

        # New method applying this monkey-patch. Note the use of:
        #
        # * "out=False", *NOT* capturing stdout.
        # * "err=True", capturing stderr.
        def _getcapture_new(self, method):
            if method == "no":
                return MultiCapture(
                    out=False, err=True, in_=False, Capture=FDCapture)
            else:
                return _getcapture_old(self, method)

        # Replace the old with the new method.
        CaptureManager._getcapture = _getcapture_new

        # Run py.test with all passed arguments.
        errno = pytest.main(self.pytest_args)
        sys.exit(errno)

Pour activer ce patch de singe, exécutez py.test comme suit:

python setup.py test -a "-s"

Stderr mais pas stdout seront désormais capturés. Nifty!

L'extension du patch de singe ci-dessus au départ de stdout et stderr est laissée au lecteur comme exercice avec un baril plein de temps libre.

Cecil Curry
la source
33

Lors de l'exécution du test, utilisez l' -soption. Toutes les instructions print dans exampletest.pyseraient imprimées sur la console lorsque le test est exécuté.

py.test exampletest.py -s
Plaisirs de l'été
la source
31

Selon la documentation de pytest , la version 3 de pytest peut désactiver temporairement la capture dans un test:

def test_disabling_capturing(capsys):
    print('this output is captured')
    with capsys.disabled():
        print('output not captured, going directly to sys.stdout')
    print('this output is also captured')
Roman Susi
la source
20

pytest capture la sortie standard des tests individuels et ne les affiche qu'à certaines conditions, ainsi que le résumé des tests qu'elle imprime par défaut.

Des informations récapitulatives supplémentaires peuvent être affichées en utilisant l'option '-r':

pytest -rP

montre la sortie capturée des tests réussis.

pytest -rx

affiche la sortie capturée des tests ayant échoué (comportement par défaut).

Le formatage de la sortie est plus joli avec -r qu'avec -s.

Sunthar
la source
2
C'est la vraie réponse que je cherchais! Je vous remercie. (Avoir la sortie standard APRÈS que les résultats du test soient souhaités. Lorsqu'ils sont entrelacés, les lignes imprimées perdent de leur valeur.)
bossylobster
18

Essayez pytest -s -v test_login.pypour plus d'informations dans la console.

-v c'est un court --verbose

-s signifie «désactiver toutes les captures»



Alex Makarenko
la source
1
si vous utilisez le fichier pytest.ini, vous pouvez utiliser: addopts = -s -v python_files = test_login.py
timj98
4

Si vous utilisez PyCharm IDE, vous pouvez exécuter ce test individuel ou tous les tests à l'aide de la barre d'outils Exécuter. La fenêtre de l'outil Exécuter affiche la sortie générée par votre application et vous pouvez y voir toutes les instructions d'impression dans le cadre de la sortie de test.

Gemini Jain
la source
Savez-vous comment imprimer PyCharm pendant le test? (au lieu de la fin du test)
Alexandre Huat
3

pytest --capture=tee-sysa été récemment ajouté. Vous pouvez capturer ainsi que voir la sortie sur stdout / err.

meonstackexchange
la source
-4

Les autres réponses ne fonctionnent pas. La seule façon de voir la sortie capturée est d'utiliser l'indicateur suivant:

pytest - montrer-capturer tout

aaa90210
la source
6
--show-capture=allest la valeur par défaut. L'ajouter n'a aucun effet.
hoefling