2015-06-08 20 views
5

Ho questo codice di esempio (test_it.py):Scartare output/error del programma in prova, ma mantenere unittest uscita

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

che mi dà il seguente risultato:

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

Ma a causa della lunghi, fastidiosi messaggi prodotti dal programma sotto test (il programma reale produce MOLTI output), non riesco a vedere l'output da unittest. Mi piacerebbe sbarazzarmi dello stdout/stderr della funzione in prova (give_me_5), ma voglio ancora vedere stdout/stderr di unittest. Vorrei ottenere questo risultato:

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

In modo che l'output prodotto dal programma in prova (sia stdout e stderr) viene filtrato da unittest, ma l'output prodotto da unittest stesso è mantenuto. Non voglio modificare il codice in fase di test (nessun reindirizzamento nel codice stesso). Voglio solo dire all'unittest che tutti gli output di stdout/stderr generati dal codice sotto test dovrebbero essere scartati.

È possibile?

+0

possibile duplicato di [python - come posso reindirizzare l'output di unittest? La soluzione ovvia non funziona] (http://stackoverflow.com/questions/14246119/python-how-can-i-redirect-the-output-of-unittest-obvious-solution-doesnt-wor) –

+0

@DougR. L'altra domanda riguarda il problema opposto: il ragazzo stava ottenendo l'output del programma e l'output di unittest diviso, che è esattamente quello che volevo ottenere. – dangonfast

risposta

4

Sostituire temporaneamente sys.stdout e sys.stderr con istanze di tipo file. Ad esempio, è possibile utilizzare i buffer di memoria StringIO.

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 

Si potrebbe desiderare di aggiungere la gestione delle eccezioni o addirittura trasformare questo codice in un contesto manager per riutilizzarlo

5

@Alik suggerimento aveva ragione. Ma immagino che potrebbe essere migliorato.

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

E l'output:

I am here 
I am back 
0

Per riferimento (e per ottenere commenti), ecco il codice che ho finito per usare (ispirato nella risposta @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() 

Ora semplicemente facendo un redirect_testcase(self), stdout/stderr viene reindirizzato a test_it.TestMe.out/test_it.TestMe.err e l'output da unittest è visibile nella console stdout/stderr (e c un essere reindirizzato tramite shell, se necessario).

C'è un problema (che non so ancora come risolvere): tutti i test in uno specifico TestCase sovrascriveranno i file .out/.err. Sarebbe opportuno aprire un file out/log diverso per ogni test (al contrario di uno comune per ogni TestCase)