web-dev-qa-db-fra.com

Python: Ecrire unittest pour l'impression sur console

La fonction foo s'imprime sur la console. Je veux tester l'impression de la console. Comment puis-je y parvenir en python?

Besoin de tester cette fonction, n'a AUCUNE déclaration de retour:

def foo(inStr):
   print "hi"+inStr

Mon test :

def test_foo():
    cmdProcess = subprocess.Popen(foo("test"), stdout=subprocess.PIPE)
    cmdOut = cmdProcess.communicate()[0]
    self.assertEquals("hitest", cmdOut)
24
sudhishkr

Vous pouvez facilement capturer la sortie standard en redirigeant simplement temporairement sys.stdout à un objet StringIO, comme suit:

import StringIO
import sys

def foo(inStr):
    print "hi"+inStr

def test_foo():
    capturedOutput = StringIO.StringIO()          # Create StringIO object
    sys.stdout = capturedOutput                   #  and redirect stdout.
    foo('test')                                   # Call unchanged function.
    sys.stdout = sys.__stdout__                   # Reset redirect.
    print 'Captured', capturedOutput.getvalue()   # Now works as before.

test_foo()

Le résultat de ce programme est:

Captured hitest

montrant que la redirection a correctement capturé la sortie et que vous avez pu restaurer le flux de sortie à ce qu'il était avant de commencer la capture.


Notez que le code ci-dessus pour Python 2.7, comme l'indique la question. Python 3 est légèrement différent:

import io
import sys

def foo(inStr):
    print ("hi"+inStr)

def test_foo():
    capturedOutput = io.StringIO()                  # Create StringIO object
    sys.stdout = capturedOutput                     #  and redirect stdout.
    foo('test')                                     # Call function.
    sys.stdout = sys.__stdout__                     # Reset redirect.
    print ('Captured', capturedOutput.getvalue())   # Now works as before.

test_foo()
39
paxdiablo

Cette réponse Python 3 utilise unittest.mock . Il utilise également une méthode d'assistance réutilisable assert_stdout, bien que cet assistant soit spécifique à la fonction testée.

import io
import unittest
import unittest.mock

from .solution import fizzbuzz


class TestFizzBuzz(unittest.TestCase):

    @unittest.mock.patch('sys.stdout', new_callable=io.StringIO)
    def assert_stdout(self, n, expected_output, mock_stdout):
        fizzbuzz(n)
        self.assertEqual(mock_stdout.getvalue(), expected_output)

    def test_only_numbers(self):
        self.assert_stdout(2, '1\n2\n')

Notez que le mock_stdout arg est passé automatiquement par le unittest.mock.patch décorateur au assert_stdout méthode.

Une classe TestStdout à usage général, éventuellement un mixage, peut en principe être dérivée de ce qui précède.

Pour ceux qui utilisent Python ≥3.4, contextlib.redirect_stdout existe également, mais il semble n'avoir aucun avantage sur unittest.mock.patch .

22
Acumenus

S'il vous arrive d'utiliser pytest , il a une capture de sortie intégrée. Exemple (tests de style pytest):

def eggs():
    print('eggs')


def test_spam(capsys):
    eggs()
    captured = capsys.readouterr()
    assert captured.out == 'eggs\n'

Vous pouvez également l'utiliser avec les classes de test unittest, bien que vous deviez passer l'objet fixture dans la classe de test, par exemple via un fixture autouse:

import unittest
import pytest


class TestSpam(unittest.TestCase):

    @pytest.fixture(autouse=True)
    def _pass_fixtures(self, capsys):
        self.capsys = capsys

    def test_eggs(self):
        eggs()
        captured = self.capsys.readouterr()
        self.assertEqual('eggs\n', captured.out)

Consultez Accès à la sortie capturée à partir d'une fonction de test pour plus d'informations.

1
hoefling