Python Unittest: Wie nur ein Teil einer Testdatei laufen?
-
21-08-2019 - |
Frage
ich eine Testdatei haben, die sehr viel Zeit nehmen, Tests enthält (sie Berechnungen zu einem Cluster senden und auf das Ergebnis warten). Alle diese sind in bestimmten Testcase-Klasse.
Da sie Zeit in Anspruch nehmen und darüber hinaus sind nicht wahrscheinlich zu brechen, würde ich mag in der Lage sein zu entscheiden, ob diese Teilmenge von Tests tut oder nicht ausgeführt (der beste Weg, mit einer Befehlszeilenargument wäre, das heißt " ./tests.py --offline
“oder so ähnlich), so konnte ich die meisten Tests oft und schnell und das ganze set ab und laufen, wenn ich Zeit habe.
Für jetzt, ich habe gerade unittest.main()
verwenden, um die Tests zu starten.
Danke.
Lösung
Der Standard unittest.main()
verwendet den Standard-Test-Loader eine Testsuite aus dem Modul zu machen, in der Haupt ausgeführt wird.
Sie müssen nicht dieses Standardverhalten verwenden.
Sie können zum Beispiel machen drei unittest.TestSuite Instanzen .
-
Die "schnelle" Teilmenge.
fast = TestSuite() fast.addTests( TestFastThis ) fast.addTests( TestFastThat )
-
Die "langsame" Teilmenge.
slow = TestSuite() slow.addTests( TestSlowAnother ) slow.addTests( TestSlowSomeMore )
-
Die "ganze" gesetzt.
alltests = unittest.TestSuite([fast, slow])
Beachten Sie, dass ich die Testcase Namen angepasst haben schnell gegen langsam anzuzeigen. Sie können Unterklasse unittest.TestLoader die Namen von Klassen zu analysieren und mehrere Lader erstellen.
Dann wird Ihr Hauptprogramm kann Befehlszeilenargumente mit optparse oder argparse (verfügbar seit 2.7 oder 3.2) zu holen, die Suite, die Sie ausführen möchten, schnell, langsam oder alle.
Alternativ können Sie darauf vertrauen, dass sys.argv[1]
einen von drei Werten ist und verwenden Sie etwas so Einfaches wie das
if __name__ == "__main__":
suite = eval(sys.argv[1]) # Be careful with this line!
unittest.TextTestRunner().run(suite)
Andere Tipps
Um nur einen einzigen spezifischen Test ausführen können Sie:
$ python -m unittest test_module.TestClass.test_method
Weitere Informationen hier
Eigentlich kann man die Namen des Testfalls als sys.argv passiert und nur diese Fälle geprüft werden.
Zum Beispiel: Angenommen, Sie haben
class TestAccount(unittest.TestCase):
...
class TestCustomer(unittest.TestCase):
...
class TestShipping(unittest.TestCase):
...
account = TestAccount
customer = TestCustomer
shipping = TestShipping
Sie können anrufen
python test.py account
nur Konto Tests haben oder sogar
$ python test.py account customer
haben beide Fälle getestet
Ich tue dies eine einfache skipIf
mit:
import os
SLOW_TESTS = int(os.getenv('SLOW_TESTS', '0'))
@unittest.skipIf(not SLOW_TESTS, "slow")
class CheckMyFeature(unittest.TestCase):
def runTest(self):
…
So kann ich nur einen bereits bestehenden Testfall mit dieser einzigen Linie dekorieren muß (keine Notwendigkeit, Test-Suiten oder ähnlich zu erstellen, nur dass man os.getenv()
Ruflinie am Anfang meiner Unit-Test-Datei), und als Standard dieses Test wird übersprungen.
Wenn ich es, obwohl sie langsam ausführen will, rufe ich gerade mein Skript wie folgt aus:
SLOW_TESTS=1 python -m unittest …
Sie haben grundsätzlich zwei Möglichkeiten, es zu tun:
- Definieren Sie eigene Reihe von Tests für die Klasse
- Erstellen Mock Klassen der Cluster-Verbindung, die aktuellen Daten angezeigt werden können.
Ich bin ein starker Befürworter der er den zweiten Ansatz; ein Unit-Test sollte Test nur eine sehr Einheit von Code, und nicht komplexe Systeme (wie Datenbanken oder Cluster). Aber ich verstehe, dass es nicht immer möglich ist; manchmal ist einfach mock ups zu schaffen zu teuer, oder das Ziel des Tests ist es wirklich in dem komplexen System.
Zurück zur Option (1), können Sie auf diese Weise vorgehen können:
suite = unittest.TestSuite()
suite.addTest(MyUnitTestClass('quickRunningTest'))
suite.addTest(MyUnitTestClass('otherTest'))
und dann die Suite zum Test Läufer übergeben:
unittest.TextTestRunner().run(suite)
Weitere Informationen über die Python-Dokumentation: http://docs.python.org /library/unittest.html#testsuite-objects
Da Sie verwenden unittest.main()
Sie nur python tests.py --help
ausführen können die Dokumentation zu erhalten:
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
Das heißt, können Sie einfach tun
python tests.py TestClass.test_method
Oder Sie können die unittest.SkipTest()
Funktion machen. Beispiel eine skipOrRunTest
Methode auf Ihre Testklasse wie folgt hinzu:
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!!!")
Dann ein Aufruf dieser Methode skipOrRunTest an den Anfang jeder Ihrer Unit-Tests wie folgt hinzu:
def testType4(self):
self.skipOrRunTest('testType4')
fand ich eine andere Lösung, basierend darauf, wie der unittest.skip
Dekorateur arbeitet.
Durch die Einstellung der __unittest_skip__
und __unittest_skip_why__
.
Label-basierende
Ich wollte ein Etikettierungssystem anwenden, einige Tests wie quick
zu beschriften, slow
, glacier
, memoryhog
, cpuhog
, core
, und so weiter.
Dann laufen all 'quick' tests
oder run everything except 'memoryhog' tests
, Ihre grundlegenden Weiße Liste / Schwarze Liste Setup
Die Umsetzung
I implementiert dies in 2 Teilen:
- Erste Beschriftungen hinzufügen, Tests (über eine benutzerdefinierte
@testlabel
Klasse Dekorateur) - Benutzerdefinierte
unittest.TestRunner
zu identifizieren, welche überspringen testet, und ändern Sie den Testliste Inhalt vor der Ausführung.
Arbeits Umsetzung ist in diesem Kern: https://gist.github.com/fragmuffin/a245f59bdcd457936c3b51aa2ebb3f6c
(ein voll funktionsfähiges Beispiel zu lang war hier zu setzen)
Das Ergebnis: ...
$ ./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)
Alle MyTests1
Klasse Tests übersprungen werden, weil es das foo
Etikett angegeben ist.
--whitelist
auch funktioniert
Schauen Sie in ein dediziertes testrunner verwenden, wie py.test, Nase oder möglicherweise sogar zope.testing. Sie haben alle Befehlszeilenoptionen zur Auswahl von Tests.
Sehen Sie zum Beispiel als Nase: https://pypi.python.org/pypi /nose/1.3.0
Ich versuchte @ slott Antwort:
if __name__ == "__main__":
suite = eval(sys.argv[1]) # Be careful with this line!
unittest.TextTestRunner().run(suite)
Aber das gab mir die folgende Fehlermeldung:
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
Die für mich folgende gearbeitet:
if __name__ == "__main__":
test_class = eval(sys.argv[1])
suite = unittest.TestLoader().loadTestsFromTestCase(test_class)
unittest.TextTestRunner().run(suite)
Ich habe einen anderen Weg gefunden, die test_ * Methoden auszuwählen, die ich nur durch Hinzufügen eines Attributs, um sie ausführen möchten. Sie verwenden im Grunde eine Metaklasse die Callables in der Testcase-Klasse zu schmücken, die die StepDebug mit einem unittest.skip Dekorateur Attribut haben. Weitere Informationen über
Skipping all Unit-Tests, sondern ein in Python von Dekorateuren und metaclasses
mitIch weiß nicht, ob es eine bessere Lösung als die oben Ich habe es als Option bin bereitstellt.
Haben Sie einen schönen Weg gefunden dies vor zu tun, so teilt hier.
Ziel: Erhalten Sie einen Satz von Testdateien zusammen, so dass sie als eine Einheit ausgeführt werden können, aber wir können immer noch einen von ihnen auswählen, indem Sie selbst auszuführen.
Problem: die Discover-Methode nicht erlaubt die einfache Auswahl eines einzigen Testfall ausführen
.Entwurf: siehe unten. Diese flacht der Namespace kann durch Testcase Klassennamen so wählen, und lassen Sie den den „tests1.test_core“ Präfix:
./run-tests TestCore.test_fmap
Code
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 )
Das ist das einzige, was für mich gearbeitet.
if __name__ == '__main__':
unittest.main( argv=sys.argv, testRunner = unittest.TextTestRunner(verbosity=2))
Als ich es genannt, obwohl ich im Namen der Klasse und Testnamen zu übergeben hatte. Ein wenig umständlich, da ich Klasse und Testname nicht Kombination gespeichert.
Python ./tests.py class_Name.test_30311
Das Entfernen des Klassennamen und Testnamen führen alle Tests in der Datei. Ich finde dies viel einfacher zu handhaben dann in Verfahren der eingebauten da ich nicht wirklich mein Kommando auf der CLI ändern. Fügen Sie einfach den Parameter.
Genießen Sie, Keith
habe ich einen Dekorateur, die für Markierungsversuche als langsame Tests und überspringen sie mit einer Umgebungsvariable
erlaubtfrom unittest import skip
import os
def slow_test(func):
return skipIf('SKIP_SLOW_TESTS' in os.environ, 'Skipping slow test')(func)
Jetzt können Sie Ihre Tests so langsam wie folgt markieren:
@slow_test
def test_my_funky_thing():
perform_test()
Und langsam Tests überspringen, indem die SKIP_SLOW_TESTS
Umgebungsvariable:
SKIP_SLOW_TESTS=1 python -m unittest