123 votes

Comment affirmer une sortie avec nosetest / unittest en python?

J'écris des tests pour une fonction comme la suivante:

 def foo():
    print 'hello world!'
 

Donc, quand je veux tester cette fonction, le code sera comme ceci:

 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!'
 

Mais si je lance nosetet avec le paramètre -s, le test échoue. Comment puis-je capturer la sortie avec le module unittest ou nasal?

131voto

Rob Kennedy Points 107381

J'ai utiliser ce gestionnaire de contexte de sortie de capture. Au final, il utilise la même technique que sur certains autres réponses qui remplace temporairement sys.stdout. Je préfère le gestionnaire de contexte car il embrasse tous la tenue de la comptabilité dans une seule fonction, donc je ne pas avoir à ré-écrire tout essayer enfin de code, et je n'ai pas à écrire l'installation et le démontage des fonctions juste pour cela.

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

L'utiliser comme ceci:

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!')

En outre, depuis l'origine de l'état de sortie est restauré à la sortie de l' with bloc, nous pouvons mettre en place une capture deuxième bloc dans la même fonction que le premier, ce qui n'est pas possible à l'aide de l'installation et le démontage des fonctions, et obtient verbeux lors de l'écriture essayez-enfin blocs manuellement. Cette capacité est venu dans maniable quand le but d'un test est de comparer les résultats de deux fonctions les uns par rapport aux autres plutôt que pour certains précalculées valeur.

65voto

Shane Hathaway Points 823

Si vous voulez vraiment faire cela, vous pouvez réaffecter sys.stdout pour la durée du test.

 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
 

Si j’écrivais ce code, cependant, je préférerais passer un paramètre optionnel out à la fonction foo .

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

Alors le test est beaucoup plus 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!'
 

48voto

FabienAndre Points 1972

Depuis la version 2.7, vous n'avez plus besoin de réaffecter sys.stdout, cela est prévu par buffer drapeau. En outre, c'est le comportement par défaut de nosetest.

Voici un exemple de défaut non tamponnée contexte:

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!')

Vous pouvez définir mémoire tampon par le biais unit2 option de ligne de commande -b, --buffer ou unittest.mainoptions. L'inverse est obtenue par nosetest pavillon --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)

17voto

sean_robbins Points 188

Je viens juste d'apprendre Python et je me suis retrouvé aux prises avec un problème similaire à celui ci-dessus avec les tests unitaires pour les méthodes avec sortie. Mon test unitaire pour le module foo ci-dessus a fini par ressembler à ceci:

 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')
 

10voto

Alison R. Points 2674

L'écriture de tests souvent nous montre une meilleure façon d'écrire notre code. Semblable à Shane de réponse, je voudrais suggérer une autre façon de voir les choses. Voulez-vous vraiment affirmer que votre programme achera une certaine chaîne de caractères, ou tout simplement qu'il construit une certaine chaîne de caractères pour la sortie? Cela devient plus facile à tester, puisque l'on peut sans doute supposer que le Python print déclaration fait son travail correctement.

def foo_msg():
    return 'hello world'

def foo():
    print foo_msg()

Ensuite, votre test est très simple:

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

Bien sûr, si vous avez vraiment besoin de tester votre programme de sortie réelle, alors n'hésitez pas à en tenir compte. :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X