17

Sto estendendo il framework Python 2.7 unittest per eseguire alcuni test di funzionalità. Una delle cose che vorrei fare è interrompere tutti i test dall'interno di un test e all'interno di un metodo setUpClass(). A volte se un test fallisce, il programma è così rotto che non è più di alcuna utilità continuare a testare, quindi voglio interrompere l'esecuzione dei test.Come interrompere tutti i test dall'interno di un test o setUp utilizzando unittest?

Ho notato che un TestResult ha un attributo shouldStop e un metodo stop(), ma non sono sicuro di come accedere a quello all'interno di un test.

Qualcuno ha qualche idea? C'è un modo migliore?

+0

Lo uso per interrompere l'esecuzione di test prima di eseguire qualsiasi test se l'applicazione in prova è configurata con le impostazioni di prod. (in realtà, con qualsiasi impostazione non di prova.) –

risposta

4

Ecco un'altra risposta mi è venuta dopo un po ':

In primo luogo, ho aggiunto una nuova eccezione:

class StopTests(Exception): 
""" 
Raise this exception in a test to stop the test run. 

""" 
    pass 

poi ho aggiunto un nuovo assert a mio figlio classe di test:

def assertStopTestsIfFalse(self, statement, reason=''): 
    try: 
     assert statement    
    except AssertionError: 
     result.addFailure(self, sys.exc_info()) 

e infine ho annullato la funzione run per includerla immediatamente sotto la chiamata testMethod():

except StopTests: 
    result.addFailure(self, sys.exc_info()) 
    result.stop() 

Mi piace di più poiché ogni test ora ha la capacità di interrompere tutti i test e non esiste un codice specifico per cpython.

+0

Dove vengono generati gli StopTest? –

3

Attualmente è possibile interrompere i test solo a livello di suite. Quando si è in un TestCase, il metodo stop() per lo TestResult non viene utilizzato durante l'iterazione attraverso i test.

Un po 'correlato alla domanda, se si utilizza python 2.7, è possibile utilizzare il flag -f/--failfast quando si chiama il test con python -m unittest. Questo fermerà il test al primo errore.

Vedi 25.3.2.1. failfast, catch and buffer command line options

Si può anche considerare l'utilizzo di Nose per eseguire i test e utilizzare il -x, --stopflag per fermare il test precoce.

+0

Grazie. Ho visto l'opzione failfast, ma non sempre voglio fallire il primo errore, solo nei posti selezionati. Credo che dovrei modificare la mia domanda per menzionare che sto usando python 2.7. – user197674

0

Ho esaminato la classe TestCase e ho deciso di creare una sottoclasse. La classe ha la precedenza su run(). Ho copiato il metodo e la linea a partire da 318 nella classe originale aggiunto questo:

# this is CPython specific. Jython and IronPython may do this differently 
if testMethod.func_code.co_argcount == 2: 
    testMethod(result) 
else: 
    testMethod() 

Ha un po 'di codice specifico CPython in là per dire se il metodo di prova può accettare un altro parametro, ma dal momento che sto usando CPython ovunque , questo non è un problema per me.

11

Nel caso in cui siete interessati, ecco un semplice esempio di come si possa prendere una decisione se stessi su uscendo da una suite di test in modo pulito con py.test:

# content of test_module.py 
import pytest 
counter = 0 
def setup_function(func): 
    global counter 
    counter += 1 
    if counter >=3: 
     pytest.exit("decided to stop the test run") 

def test_one(): 
    pass 
def test_two(): 
    pass 
def test_three(): 
    pass 

e se si esegue questo si ottiene:

$ pytest test_module.py 
============== test session starts ================= 
platform linux2 -- Python 2.6.5 -- pytest-1.4.0a1 
test path 1: test_module.py 

test_module.py .. 

!!!! Exit: decided to stop the test run !!!!!!!!!!!! 
============= 2 passed in 0.08 seconds ============= 

È inoltre possibile inserire la chiamata py.test.exit() all'interno di un test o in un plug-in specifico del progetto.

Sidenote: py.test supporta nativamente py.test --maxfail=NUM per implementare l'arresto dopo NUM fallimenti.

Sidenote2: py.test ha un supporto limitato per l'esecuzione di test nello stile tradizionale unittest.TestCase.

+0

Grazie. Non ero a conoscenza di quel quadro di test. Darei un'occhiata a questo. – user197674

+2

Versione attuale di Py.Test: importa pytest e poi puoi fare pytest.exit ("il tuo messaggio") – RvdK

+1

Questo funziona bene anche all'interno di un dispositivo più fittizio dichiarato in un file conftest.py, con scope = 'session', autouse = Vero. Questo verrà eseguito prima di ogni test nel progetto. Chiamando pytest.exit da qui è possibile interrompere l'intera esecuzione del test prima di eseguire qualsiasi test. Lo uso per abortire i test se sono in esecuzione utilizzando prod config (in pratica qualsiasi configurazione non di prova) –

1

Nel ciclo di prova di unittest.TestSuite, c'è una condizione break alla partenza:

class TestSuite(BaseTestSuite): 

    def run(self, result, debug=False): 
     topLevel = False 
     if getattr(result, '_testRunEntered', False) is False: 
      result._testRunEntered = topLevel = True 

     for test in self: 
      if result.shouldStop: 
       break 

Così sto usando una suite di test personalizzato come questo:

class CustomTestSuite(unittest.TestSuite): 
    """ This variant registers the test result object with all ScriptedTests, 
     so that a failed Loign test can abort the test suite by setting result.shouldStop 
     to True 
    """ 
    def run(self, result, debug=False): 
     for test in self._tests: 
      test.result = result 

     return super(CustomTestSuite, self).run(result, debug) 

con un risultato del test personalizzato classe come questa:

class CustomTestResult(TextTestResult): 
    def __init__(self, stream, descriptions, verbosity): 
     super(CustomTestResult, self).__init__(stream, descriptions, verbosity) 
     self.verbosity = verbosity 
     self.shouldStop = False 

e le mie classi di test sono come:

class ScriptedTest(unittest.TestCase): 
    def __init__(self, environment, test_cfg, module, test): 
     super(ScriptedTest, self).__init__() 
     self.result = None 

In determinate condizioni, interrompere quindi la suite di test; per esempio, la suite di test inizia con un account di accesso, e se non funziona, non c'è bisogno di provare il resto:

try: 
     test_case.execute_script(test_command_list) 
    except AssertionError as e: 
     if test_case.module == 'session' and test_case.test == 'Login': 
      test_case.result.shouldStop = True 
      raise TestFatal('Login failed, aborting test.') 
     else: 
      raise sys.exc_info() 

allora io uso la suite di test nel modo seguente:

suite = CustomTestSuite() 

    self.add_tests(suite) 

    result = unittest.TextTestRunner(verbosity=self.environment.verbosity, stream=UnitTestLoggerStream(self.logger), 
            resultclass=CustomTestResult).run(suite) 

Non sono sicuro se c'è un modo migliore per farlo, ma si comporta correttamente per i miei test.

0

Anche se non si ottengono i soliti rapporti di prova dei test eseguiti finora, un modo molto semplice per interrompere l'esecuzione del test all'interno di un metodo TestCase è semplicemente quello di aumentare KeyboardInterrupt all'interno del metodo.

Si può vedere come è consentito solo KeyboardInterrupt a ribollire dentro di prova corridore unittest s', cercando in codice di CPython here all'interno testPartExecutor().

Problemi correlati