2015-06-08 12 views
5

Ich habe diesen Beispielcode (test_it.py):Discard stdout/stderr von Programm im Test, aber halten Unittest Ausgabe

import sys 

def give_me_5(): 
    print >>sys.stdout, "STDOUT" 
    print >>sys.stderr, "STDERR" 
    return 6 

import unittest 


class TestMe(unittest.TestCase): 

    def setUp(self): 
     pass 

    def test_give_me_5(self): 
     self.assertEqual(give_me_5(), 5) 


if __name__ == '__main__': 
    unittest.main() 

die mir die folgende Ausgabe gibt:

» python -m unittest test_it 
A long annoying output message 
A long annoying error message 
F 
====================================================================== 
FAIL: test_give_me_5 (__main__.TestMe) 
---------------------------------------------------------------------- 
Traceback (most recent call last): 
    File "xx.py", line 17, in test_give_me_5 
    self.assertEqual(give_me_5(), 5) 
AssertionError: 6 != 5 

---------------------------------------------------------------------- 
Ran 1 test in 0.000s 

FAILED (failures=1) 

Aber wegen der lange, lästige Nachrichten produziert von dem Programm im Test (das echte Programm produziert viele Ausgänge), ich bin nicht in der Lage, die Ausgabe von unittest zu sehen. Ich würde gerne den stdout/stderr der zu testenden Funktion loswerden (give_me_5), aber ich möchte immer noch stdout/stderr von unittest sehen. Ich möchte dieses Ergebnis erhalten:

» python -m unittest test_it 
F 
====================================================================== 
FAIL: test_give_me_5 (__main__.TestMe) 
---------------------------------------------------------------------- 
Traceback (most recent call last): 
    File "xx.py", line 17, in test_give_me_5 
    self.assertEqual(give_me_5(), 5) 
AssertionError: 6 != 5 

---------------------------------------------------------------------- 
Ran 1 test in 0.000s 

FAILED (failures=1) 

Damit die Ausgabe durch das Programm im Test (beide stdout und stderr) erzeugt wird, durch Unittest herausgefiltert, aber die Ausgabe produziert von Unittest selbst gehalten wird. Ich möchte den zu testenden Code nicht ändern (keine Umleitung im Code selbst). Ich möchte nur unittest sagen, dass alle Ausgaben zu stdout/stderr, die durch den getesteten Code erzeugt werden, verworfen werden sollten.

Ist das möglich?

+0

mögliches Duplikat von [python - Wie kann ich die Ausgabe von unittest umleiten? Offensichtliche Lösung funktioniert nicht] (http://stackoverflow.com/questions/14246119/python-how-can-i-redirect-the-output-of-unittest-obvious-solution-doesnt-wor) –

+0

@DougR. Diese andere Frage betrifft das gegenteilige Problem: Der Typ hat die Ausgabe des Programms und die Ausgabe von unitest geteilt, was genau das ist, was ich erreichen wollte. – dangonfast

Antwort

4

Vorübergehend ersetzen sys.stdout und sys.stderr durch Datei-ähnliche Instanzen. Zum Beispiel können Sie StringIO alias Speicherpuffer verwenden.

from StringIO import StringIO 

..... 


class TestMe(unittest.TestCase): 

    def setUp(self): 
     pass 

    def test_give_me_5(self): 

     stdout = sys.stdout 
     stderr = sys.stderr 

     sys.stdout = StringIO() 
     sys.stderr = StringIO() 

     self.assertEqual(give_me_5(), 5) 

     sys.stdout = stdout 
     sys.stderr = stderr 

Vielleicht möchten Sie die Ausnahmebehandlung hinzugefügt werden sollen oder auch diesen Code in einem Kontext-Manager drehen

5

@Alik Vorschlag richtig war wieder zu verwenden. Aber ich denke, es könnte verbessert werden.

import sys 
# StringIO is replaced in Python 3: 
try: 
    from StringIO import StringIO 
except ImportError: 
    from io import StringIO 

class ReplaceStd(object): 
    """ Let's make it pythonic. """ 

    def __init__(self): 
     self.stdout = None 
     self.stderr = None 

    def __enter__(self): 
     self.stdout = sys.stdout 
     self.stderr = sys.stderr 

     # as it was suggseted already: 
     sys.stdout = StringIO() 
     sys.stderr = StringIO() 

    def __exit__(self, type, value, traceback): 
     sys.stdout = self.stdout 
     sys.stderr = self.stderr 

print('I am here') 
with ReplaceStd(): 
    print('I am not') 

print('I am back') 

Und die Ausgabe:

I am here 
I am back 
0

Als Referenz (und Kommentare zu erhalten), hier ist der Code, den ich beendet habe mit bis (inspiriert in der Antwort von @Alik):

import sys 

def give_me_5(): 
    print >>sys.stdout, "A long annoying output message" 
    print >>sys.stderr, "A long annoying error message" 
    return 6 


import unittest 

def redirect(stdout_file=None, stderr_file=None): 
    new_stdout = open(stdout_file, 'w') if stdout_file else None 
    new_stderr = open(stderr_file, 'w') if stderr_file else None 
    if new_stdout : sys.stdout = new_stdout 
    if new_stderr : sys.stderr = new_stderr 

def redirect_testcase(test_case): 
    logfile = '.'.join([test_case.__module__, test_case.__class__.__name__, 'out']) 
    errfile = '.'.join([test_case.__module__, test_case.__class__.__name__, 'err']) 
    redirect(logfile, errfile) 


class TestMe(unittest.TestCase): 

    def setUp(self): 
     redirect_testcase(self) 

    def test_give_me_5(self): 
     self.assertEqual(give_me_5(), 5) 

if __name__ == '__main__': 
    unittest.main() 

Jetzt einfach durch eine redirect_testcase(self), stdout/stderr umgeleitet werden zu test_it.TestMe.out/test_it.TestMe.err, und die Ausgabe von unittest ist in der Konsole stdout/stderr (und c Bei Bedarf wird eine Weiterleitung über die Shell vorgenommen.

Es gibt ein Problem (das ich noch nicht beheben kann): alle Tests in einem bestimmten Testfall überschreiben die .out/.err Dateien. Es wäre gut, für jeden Test eine andere out/log Datei zu öffnen (im Gegensatz zu einer gewöhnlichen für jeden Testfall).