454 Stimmen

Wie kann ich alle Python-Unit-Tests in einem Verzeichnis ausführen?

Ich habe ein Verzeichnis, das meine Python-Unit-Tests enthält. Jedes Unit-Test-Modul hat die Form test_*.py . Ich versuche, eine Datei namens all_test.py die, Sie haben es erraten, alle Dateien in der oben erwähnten Testform ausführt und das Ergebnis zurückgibt. Ich habe bisher zwei Methoden ausprobiert; beide sind fehlgeschlagen. Ich werde die beiden Methoden zeigen und hoffe, dass jemand da draußen weiß, wie man das richtig macht.

Bei meinem ersten tapferen Versuch dachte ich: "Wenn ich einfach alle meine Testmodule in die Datei importiere und diese dann aufrufe unittest.main() Dingsbums, das funktioniert doch, oder?" Nun, es stellte sich heraus, dass ich falsch lag.

import glob
import unittest

testSuite = unittest.TestSuite()
test_file_strings = glob.glob('test_*.py')
module_strings = [str[0:len(str)-3] for str in test_file_strings]

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

Das hat nicht funktioniert, das Ergebnis war:

$ python all_test.py 

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

Bei meinem zweiten Versuch dachte ich mir, ok, vielleicht versuche ich, diese ganze Testsache auf eine mehr "manuelle" Art und Weise zu machen. Also habe ich versucht, das unten zu tun:

import glob
import unittest

testSuite = unittest.TestSuite()
test_file_strings = glob.glob('test_*.py')
module_strings = [str[0:len(str)-3] for str in test_file_strings]
[__import__(str) for str in module_strings]
suites = [unittest.TestLoader().loadTestsFromName(str) for str in module_strings]
[testSuite.addTest(suite) for suite in suites]
print testSuite 

result = unittest.TestResult()
testSuite.run(result)
print result

#Ok, at this point I have a result
#How do I display it as the normal unit test command line output?
if __name__ == "__main__":
    unittest.main()

Auch das hat nicht funktioniert, aber es scheint so nahe dran zu sein!

$ python all_test.py 
<unittest.TestSuite tests=[<unittest.TestSuite tests=[<unittest.TestSuite tests=[<test_main.TestMain testMethod=test_respondes_to_get>]>]>]>
<unittest.TestResult run=1 errors=0 failures=0>

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

Ich scheine eine Art von Suite zu haben, und ich kann das Ergebnis ausführen. Ich bin etwas besorgt über die Tatsache, dass es heißt, ich hätte nur run=1 scheint das zu sein run=2 aber es ist ein Fortschritt. Aber wie kann ich das Ergebnis an main übergeben und anzeigen? Oder wie bekomme ich es grundsätzlich so hin, dass ich nur diese Datei ausführen kann und dabei alle Unit-Tests in diesem Verzeichnis ausführe?

690voto

Travis Bear Punkte 11721

Mit Python 2.7 und höher müssen Sie dafür keinen neuen Code schreiben oder Tools von Drittanbietern verwenden; die rekursive Testausführung über die Befehlszeile ist bereits integriert. Setzen Sie eine __init__.py in Ihrem Testverzeichnis und:

python -m unittest discover <test_directory>
# or
python -m unittest discover -s <directory> -p '*_test.py'

Weitere Informationen finden Sie in der python 2.7 oder python 3.x unittest-Dokumentation.


Aktualisierung für 2021:

Viele moderne Python-Projekte verwenden fortgeschrittenere Werkzeuge wie pytest . Zum Beispiel, nach unten ziehen matplotlib o scikit-learn und Sie werden sehen, dass sie beide es benutzen.

Es ist wichtig, diese neueren Werkzeuge zu kennen, denn wenn man mehr als 7000 Tests hat, braucht man sie:

  • fortgeschrittenere Möglichkeiten, um zusammenzufassen, was bestanden, übersprungen, Warnungen, Fehler
  • einfache Möglichkeiten zu erkennen, wie sie gescheitert sind
  • Prozent abgeschlossen, da es läuft
  • Gesamtlaufzeit
  • Möglichkeiten zur Erstellung eines Prüfberichts
  • usw. usw.

247voto

tmck-code Punkte 2161

In Python 3, wenn Sie die Funktion unittest.TestCase :

  • Sie müssen eine leere (oder andere) __init__.py Datei in Ihrer test Verzeichnis ( muss genannt werden test/ )
  • Ihre Testdateien in test/ dem Muster entsprechen test_*.py . Sie können sich in einem Unterverzeichnis unter test/ , und diese Unterverzeichnisse können beliebig benannt werden.

Dann können Sie alle Tests mit durchführen:

python -m unittest

Erledigt! Eine Lösung mit weniger als 100 Zeilen. Hoffentlich spart ein anderer Python-Anfänger Zeit, indem er dies findet.

111voto

Ned Batchelder Punkte 342778

Sie könnten einen Testrunner verwenden, der dies für Sie erledigt. Nase ist zum Beispiel sehr gut. Wenn es ausgeführt wird, findet es Tests im aktuellen Baum und führt sie aus.

Aktualisiert:

Hier ist noch etwas Code aus meiner Zeit vor der Nase. Sie wollen wahrscheinlich nicht die explizite Liste der Modulnamen, aber vielleicht ist der Rest für Sie nützlich.

testmodules = [
    'cogapp.test_makefiles',
    'cogapp.test_whiteutils',
    'cogapp.test_cogapp',
    ]

suite = unittest.TestSuite()

for t in testmodules:
    try:
        # If the module defines a suite() function, call it to get the suite.
        mod = __import__(t, globals(), locals(), ['suite'])
        suitefn = getattr(mod, 'suite')
        suite.addTest(suitefn())
    except (ImportError, AttributeError):
        # else, just load all the test cases from the module.
        suite.addTest(unittest.defaultTestLoader.loadTestsFromName(t))

unittest.TextTestRunner().run(suite)

89voto

slaughter98 Punkte 1567

Dies ist nun direkt von Unittest aus möglich: unittest.TestLoader.discover .

import unittest
loader = unittest.TestLoader()
start_dir = 'path/to/your/test/files'
suite = loader.discover(start_dir)

runner = unittest.TextTestRunner()
runner.run(suite)

33voto

Stephen Cagle Punkte 13277

Nun, wenn man den obigen Code ein wenig studiert (insbesondere mit TextTestRunner y defaultTestLoader ), konnte ich ziemlich nah herankommen. Schließlich korrigierte ich meinen Code, indem ich alle Testsuiten an einen einzigen Suites-Konstruktor übergab, anstatt sie "manuell" hinzuzufügen, wodurch meine anderen Probleme behoben wurden. Hier ist also meine Lösung.

import glob
import unittest

test_files = glob.glob('test_*.py')
module_strings = [test_file[0:len(test_file)-3] for test_file in test_files]
suites = [unittest.defaultTestLoader.loadTestsFromName(test_file) for test_file in module_strings]
test_suite = unittest.TestSuite(suites)
test_runner = unittest.TextTestRunner().run(test_suite)

Ja, es ist wahrscheinlich einfacher, nur die Nase zu benutzen, als dies zu tun, aber das ist nicht der Punkt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X