Python unittest: come eseguire solo una parte di un file di prova?
-
21-08-2019 - |
Domanda
Ho un file di test che contiene i test che prendono un bel po 'di tempo (mandano i calcoli a un cluster e attendere il risultato). Tutti questi sono in specifica classe di TestCase.
Dal momento che richiedono tempo e, inoltre, non sono suscettibili di rompersi, avrei voluto essere in grado di scegliere se questo sottoinsieme di test fa o non funziona (il modo migliore sarebbe con un argomento della riga di comando, vale a dire " ./tests.py --offline
" o qualcosa di simile), così ho potuto eseguire la maggior parte dei test, spesso e rapidamente e l'intero set di tanto in tanto, quando ho tempo.
Per il momento, mi basta usare unittest.main()
per iniziare i test.
Grazie.
Soluzione
Il predefinito unittest.main()
utilizza il caricatore test di default per effettuare una TestSuite dal modulo principale in cui è in esecuzione.
Non è necessario utilizzare questo comportamento predefinito.
È possibile, ad esempio, fare tre unittest.TestSuite casi .
-
Il sottoinsieme "veloce".
fast = TestSuite() fast.addTests( TestFastThis ) fast.addTests( TestFastThat )
-
Il "lento" sottoinsieme.
slow = TestSuite() slow.addTests( TestSlowAnother ) slow.addTests( TestSlowSomeMore )
-
L ' "intero" set.
alltests = unittest.TestSuite([fast, slow])
Si noti che Ho modificato i nomi TestCase per indicare veloci o lente. È possibile creare una sottoclasse unittest.TestLoader per analizzare i nomi delle classi e creare più caricatori.
Poi il programma principale in grado di analizzare gli argomenti della riga di comando con optparse o argparse (disponibile dal 2.7 o 3.2) di scegliere quale camera che si desidera eseguire, veloce, lento o tutti.
In alternativa, ci si può fidare che sys.argv[1]
è uno dei tre valori e utilizzare qualcosa di semplice come questo
if __name__ == "__main__":
suite = eval(sys.argv[1]) # Be careful with this line!
unittest.TextTestRunner().run(suite)
Altri suggerimenti
Per eseguire un solo test specifico è possibile utilizzare:
$ python -m unittest test_module.TestClass.test_method
Maggiori informazioni qui
In realtà, si può passare i nomi dei test case come sys.argv e solo i casi saranno testati.
Per esempio, supponiamo di avere
class TestAccount(unittest.TestCase):
...
class TestCustomer(unittest.TestCase):
...
class TestShipping(unittest.TestCase):
...
account = TestAccount
customer = TestCustomer
shipping = TestShipping
È possibile chiamare
python test.py account
avere solo i test di conto, o anche
$ python test.py account customer
di avere entrambi i casi testati
Sto facendo questo con un semplice skipIf
:
import os
SLOW_TESTS = int(os.getenv('SLOW_TESTS', '0'))
@unittest.skipIf(not SLOW_TESTS, "slow")
class CheckMyFeature(unittest.TestCase):
def runTest(self):
…
In questo modo ho solo bisogno di decorare un banco di prova già esistente con questa singola linea (non c'è bisogno di creare suite di test o simili, solo che una os.getenv()
linea di chiamata all'inizio del mio file di test di unità), e come impostazione predefinita questo test viene saltato.
Se voglio eseguirlo nonostante sia lento, mi basta chiamare il mio script come questo:
SLOW_TESTS=1 python -m unittest …
Hai fondamentalmente due modi per farlo:
- Definisci la propria suite di test per la classe
- Creare classi finte della connessione cluster che restituirà i dati effettivi.
Sono un forte sostenitore di lui secondo approccio; una prova di unità dovrebbe di prova solo un'unità di codice, e non sistemi complessi (come i database o cluster). Ma mi rendo conto che non è sempre possibile; a volte, la creazione di mock up è semplicemente troppo costoso, o l'obiettivo del test è davvero nel sistema complesso.
Torna ad opzione (1), si può procedere in questo modo:
suite = unittest.TestSuite()
suite.addTest(MyUnitTestClass('quickRunningTest'))
suite.addTest(MyUnitTestClass('otherTest'))
e poi passando la suite al test runner:
unittest.TextTestRunner().run(suite)
Maggiori informazioni sulla documentazione python: http://docs.python.org /library/unittest.html#testsuite-objects
Dal momento che si utilizza unittest.main()
si può semplicemente eseguire python tests.py --help
per ottenere la documentazione:
Usage: tests.py [options] [test] [...]
Options:
-h, --help Show this message
-v, --verbose Verbose output
-q, --quiet Minimal output
-f, --failfast Stop on first failure
-c, --catch Catch control-C and display results
-b, --buffer Buffer stdout and stderr during test runs
Examples:
tests.py - run default set of tests
tests.py MyTestSuite - run suite 'MyTestSuite'
tests.py MyTestCase.testSomething - run MyTestCase.testSomething
tests.py MyTestCase - run all 'test*' test methods
in MyTestCase
Cioè, si può semplicemente fare
python tests.py TestClass.test_method
In alternativa, è possibile utilizzare la funzione unittest.SkipTest()
. Esempio, aggiungere un metodo skipOrRunTest
alla classe di test in questo modo:
def skipOrRunTest(self,testType):
#testsToRun = 'ALL'
#testsToRun = 'testType1, testType2, testType3, testType4,...etc'
#testsToRun = 'testType1'
#testsToRun = 'testType2'
#testsToRun = 'testType3'
testsToRun = 'testType4'
if ((testsToRun == 'ALL') or (testType in testsToRun)):
return True
else:
print "SKIPPED TEST because:\n\t testSuite '" + testType + "' NOT IN testsToRun['" + testsToRun + "']"
self.skipTest("skipppy!!!")
Quindi aggiungere una chiamata a questo metodo skipOrRunTest al proprio all'inizio di ciascuno dei vostri test di unità in questo modo:
def testType4(self):
self.skipOrRunTest('testType4')
ho trovato un'altra soluzione, in base a come funziona il unittest.skip
decoratore.
Impostando il __unittest_skip__
e __unittest_skip_why__
.
basata sulle etichette
ho voluto applicare un sistema di etichettatura, per etichettare alcuni test come quick
, slow
, glacier
, memoryhog
, cpuhog
, core
, e così via.
Quindi eseguire all 'quick' tests
, o run everything except 'memoryhog' tests
, la configurazione di base whitelist / blacklist
Implementazione
ho implementato questo in 2 parti:
- Per prima cosa aggiungere etichette alle prove (tramite un costume
@testlabel
classe decoratore) - Custom
unittest.TestRunner
per identificare quali test saltare, e modificare il contenuto-TEST prima di eseguire.
implementazione di lavoro è in questo succo: https://gist.github.com/fragmuffin/a245f59bdcd457936c3b51aa2ebb3f6c
(un esempio completamente funzionante era troppo lungo per mettere qui)
Il risultato è ...
$ ./runtests.py --blacklist foo
test_foo (test_things.MyTest2) ... ok
test_bar (test_things.MyTest3) ... ok
test_one (test_things.MyTests1) ... skipped 'label exclusion'
test_two (test_things.MyTests1) ... skipped 'label exclusion'
----------------------------------------------------------------------
Ran 4 tests in 0.000s
OK (skipped=2)
Tutti i test di classe MyTests1
vengono saltati perché ha la foo
etichetta.
--whitelist
funziona anche
considerare di usare un testrunner dedicato, come py.test, naso o forse anche zope.testing. Hanno tutte le opzioni della riga di comando per la selezione di test.
Cercare per esempio come Profumo: https://pypi.python.org/pypi /nose/1.3.0
Ho provato @ risposta di Slott:
if __name__ == "__main__":
suite = eval(sys.argv[1]) # Be careful with this line!
unittest.TextTestRunner().run(suite)
Ma che mi ha dato il seguente errore:
Traceback (most recent call last):
File "functional_tests.py", line 178, in <module>
unittest.TextTestRunner().run(suite)
File "/usr/lib/python2.7/unittest/runner.py", line 151, in run
test(result)
File "/usr/lib/python2.7/unittest/case.py", line 188, in __init__
testMethod = getattr(self, methodName)
TypeError: getattr(): attribute name must be string
Di seguito ha lavorato per me:
if __name__ == "__main__":
test_class = eval(sys.argv[1])
suite = unittest.TestLoader().loadTestsFromTestCase(test_class)
unittest.TextTestRunner().run(suite)
Ho trovato un altro modo per selezionare il test_ * metodi che ho solo voglia di correre con l'aggiunta di un attributo a loro. Che, fondamentalmente, utilizza un metaclasse per decorare le callable all'interno della classe TestCase che hanno l'attributo StepDebug con un decoratore unittest.skip. Maggiori informazioni
saltare tutti i test unitari ma uno in Python utilizzando decoratori e metaclassi
Non so se si tratta di una soluzione migliore rispetto a quelli di cui sopra sto solo dotandola come opzione.
Non ho trovato un bel modo per farlo prima, in modo da condividere qui.
Obiettivo: ottenere una serie di file di test insieme in modo che possano essere eseguiti come un'unità, ma possiamo ancora scegliere uno qualsiasi di essi a correre da solo.
Problema: il metodo di scoperta non consente una facile selezione di un unico banco di prova per eseguire
.Design: vedi sotto. Questo appiattisce lo spazio dei nomi in modo può selezionare da TestCase nome della classe, e lasciare fuori il il "tests1.test_core" prefisso:
./run-tests TestCore.test_fmap
Codice
test_module_names = [
'tests1.test_core',
'tests2.test_other',
'tests3.test_foo',
]
loader = unittest.defaultTestLoader
if args:
alltests = unittest.TestSuite()
for a in args:
for m in test_module_names:
try:
alltests.addTest( loader.loadTestsFromName( m+'.'+a ) )
except AttributeError as e:
continue
else:
alltests = loader.loadTestsFromNames( test_module_names )
runner = unittest.TextTestRunner( verbosity = opt.verbose )
runner.run( alltests )
Questa è l'unica cosa che ha funzionato per me.
if __name__ == '__main__':
unittest.main( argv=sys.argv, testRunner = unittest.TextTestRunner(verbosity=2))
Quando ho chiamato anche se ho dovuto passare il nome del nome della classe e il test. Un po 'scomodo in quanto non ho combinazione di classe e nome del test memorizzato.
python ./tests.py class_Name.test_30311
Rimozione del nome della classe e nome del test viene eseguito tutti i test nel file. Trovo che questo sia molto più facile da affrontare poi il costruito nel metodo dal momento che in realtà non cambio il mio comando sul CLI. Basta aggiungere il parametro.
Enjoy, Keith
ho creato un decoratore che permette di marcatura test come test lenti e saltare utilizzando una variabile d'ambiente
from unittest import skip
import os
def slow_test(func):
return skipIf('SKIP_SLOW_TESTS' in os.environ, 'Skipping slow test')(func)
Ora è possibile contrassegnare i test più lento in questo modo:
@slow_test
def test_my_funky_thing():
perform_test()
E saltare test lenti impostando la variabile SKIP_SLOW_TESTS
ambiente:
SKIP_SLOW_TESTS=1 python -m unittest