Erste Schritte mit dem Testen in Python
Dieses Tutorial richtet sich an alle, die eine fantastische Anwendung in Python geschrieben haben, aber noch keine Tests geschrieben haben.
Das Testen in Python ist ein großes Thema und kann sehr komplex sein, muss aber nicht schwierig sein. Sie können in wenigen einfachen Schritten einfache Tests für Ihre Anwendung erstellen und von dort aus darauf aufbauen.
In diesem Tutorial erfahren Sie, wie Sie einen Basistest erstellen, ausführen und die Fehler finden, bevor Ihre Benutzer dies tun! Sie lernen die Tools kennen, mit denen Sie Tests schreiben und ausführen, die Leistung Ihrer Anwendung überprüfen und sogar nach Sicherheitsproblemen suchen können.
Free Bonus:5 Thoughts On Python Mastery, ein kostenloser Kurs für Python-Entwickler, der Ihnen die Roadmap und die Denkweise zeigt, die Sie benötigen, um Ihre Python-Fähigkeiten auf die nächste Stufe zu bringen.
Testen Sie Ihren Code
Es gibt viele Möglichkeiten, Ihren Code zu testen. In diesem Tutorial lernen Sie die Techniken aus den grundlegendsten Schritten kennen und arbeiten an fortgeschrittenen Methoden.
Automatisiert vs. Manuelle Prüfung
Die gute Nachricht ist, dass Sie wahrscheinlich bereits einen Test erstellt haben, ohne es zu merken. Erinnerst du dich, als du deine Anwendung ausgeführt und zum ersten Mal verwendet hast? Haben Sie die Funktionen überprüft und damit experimentiert? Dies wird alsexploratory testing bezeichnet und ist eine Form des manuellen Testens.
Erkundungstests sind eine Form von Tests, die ohne Plan durchgeführt werden. In einem Erkundungstest untersuchen Sie nur die Anwendung.
Um einen vollständigen Satz manueller Tests zu erhalten, müssen Sie lediglich eine Liste aller Funktionen Ihrer Anwendung, der verschiedenen Arten von Eingaben, die sie akzeptieren kann, und der erwarteten Ergebnisse erstellen. Jedes Mal, wenn Sie Änderungen an Ihrem Code vornehmen, müssen Sie jedes einzelne Element in dieser Liste durchgehen und überprüfen.
Das klingt nicht nach viel Spaß, oder?
Hier kommt das automatisierte Testen ins Spiel. Automatisiertes Testen ist die Ausführung Ihres Testplans (die Teile Ihrer Anwendung, die Sie testen möchten, die Reihenfolge, in der Sie sie testen möchten, und die erwarteten Antworten) durch ein Skript anstelle eines Menschen. Python enthält bereits eine Reihe von Tools und Bibliotheken, mit denen Sie automatisierte Tests für Ihre Anwendung erstellen können. Wir werden diese Tools und Bibliotheken in diesem Tutorial untersuchen.
Unit Tests vs. Integrationstests
In der Welt des Testens mangelt es nicht an Terminologie. Jetzt, da Sie den Unterschied zwischen automatisierten und manuellen Tests kennen, ist es an der Zeit, eine Ebene tiefer zu gehen.
Überlegen Sie, wie Sie die Lichter eines Autos testen könnten. Sie würden die Lichter einschalten (bekannt alstest step) und aus dem Auto gehen oder einen Freund bitten, zu überprüfen, ob die Lichter an sind (bekannt alstest assertion). Das Testen mehrerer Komponenten wird alsintegration testing bezeichnet.
Denken Sie an alle Dinge, die richtig funktionieren müssen, damit eine einfache Aufgabe das richtige Ergebnis liefert. Diese Komponenten sind wie die Teile Ihrer Anwendung, all die Klassen, Funktionen und Module, die Sie geschrieben haben.
Eine große Herausforderung beim Integrationstest besteht darin, dass ein Integrationstest nicht das richtige Ergebnis liefert. Es ist sehr schwierig, das Problem zu diagnostizieren, ohne feststellen zu können, welcher Teil des Systems ausfällt. Wenn die Lichter nicht eingeschaltet wurden, sind möglicherweise die Glühbirnen defekt. Ist die Batterie leer? Was ist mit der Lichtmaschine? Fällt der Computer des Autos aus?
Wenn Sie ein schickes modernes Auto haben, wird es Ihnen sagen, wann Ihre Glühbirnen weg sind. Dies geschieht mit einer Form vonunit test.
Ein Komponententest ist ein kleinerer Test, bei dem überprüft wird, ob eine einzelne Komponente ordnungsgemäß funktioniert. Mithilfe eines Komponententests können Sie Fehler in Ihrer Anwendung isolieren und schneller beheben.
Sie haben gerade zwei Arten von Tests gesehen:
-
Ein Integrationstest überprüft, ob Komponenten in Ihrer Anwendung miteinander arbeiten.
-
Ein Komponententest überprüft eine kleine Komponente in Ihrer Anwendung.
Sie können sowohl Integrationstests als auch Komponententests in Python schreiben. Um einen Komponententest für die integrierte Funktionsum()
zu schreiben, würden Sie die Ausgabe vonsum()
mit einer bekannten Ausgabe vergleichen.
So überprüfen Sie beispielsweise, obsum()
der Zahlen(1, 2, 3)
gleich6
sind:
>>>
>>> assert sum([1, 2, 3]) == 6, "Should be 6"
Dies gibt nichts auf der REPL aus, da die Werte korrekt sind.
Wenn das Ergebnis vonsum()
falsch ist, schlägt dies mitAssertionError
und der Meldung"Should be 6"
fehl. Versuchen Sie erneut eine Assertionsanweisung mit den falschen Werten, um einAssertionError
zu sehen:
>>>
>>> assert sum([1, 1, 1]) == 6, "Should be 6"
Traceback (most recent call last):
File "", line 1, in
AssertionError: Should be 6
In der REPL sehen Sie die erhöhtenAssertionError
, da das Ergebnis vonsum()
nicht mit6
übereinstimmt.
Anstatt die REPL zu testen, möchten Sie diese in eine neue Python-Datei mit dem Namentest_sum.py
einfügen und erneut ausführen:
def test_sum():
assert sum([1, 2, 3]) == 6, "Should be 6"
if __name__ == "__main__":
test_sum()
print("Everything passed")
Jetzt haben Sie eintest case, eine Zusicherung und einen Einstiegspunkt (die Befehlszeile) geschrieben. Sie können dies jetzt in der Befehlszeile ausführen:
$ python test_sum.py
Everything passed
Sie können das erfolgreiche ErgebnisEverything passed
sehen.
In Python akzeptiertsum()
jedes iterable als erstes Argument. Sie haben mit einer Liste getestet. Testen Sie jetzt auch mit einem Tupel. Erstellen Sie eine neue Datei mit dem Namentest_sum_2.py
mit dem folgenden Code:
def test_sum():
assert sum([1, 2, 3]) == 6, "Should be 6"
def test_sum_tuple():
assert sum((1, 2, 2)) == 6, "Should be 6"
if __name__ == "__main__":
test_sum()
test_sum_tuple()
print("Everything passed")
Wenn Sietest_sum_2.py
ausführen, gibt das Skript einen Fehler aus, dasum()
von(1, 2, 2)
5
und nicht6
ist. Das Ergebnis des Skripts enthält die Fehlermeldung, die Codezeile und den Traceback:
$ python test_sum_2.py
Traceback (most recent call last):
File "test_sum_2.py", line 9, in
test_sum_tuple()
File "test_sum_2.py", line 5, in test_sum_tuple
assert sum((1, 2, 2)) == 6, "Should be 6"
AssertionError: Should be 6
Hier können Sie sehen, wie ein Fehler in Ihrem Code einen Fehler auf der Konsole verursacht, mit einigen Informationen darüber, wo der Fehler war und was das erwartete Ergebnis war.
Das Schreiben von Tests auf diese Weise ist für eine einfache Überprüfung in Ordnung, aber was ist, wenn mehr als einer fehlschlägt? Hier kommen Testläufer ins Spiel. Der Test Runner ist eine spezielle Anwendung, mit der Tests ausgeführt, die Ausgabe überprüft und Tools zum Debuggen und Diagnostizieren von Tests und Anwendungen bereitgestellt werden können.
Auswahl eines Testläufers
Für Python stehen viele Testläufer zur Verfügung. Die in die Python-Standardbibliothek integrierte Version heißtunittest
. In diesem Tutorial verwenden Sie die Testfälle vonunittest
und den Testläufer vonunittest
. Die Prinzipien vonunittest
lassen sich leicht auf andere Frameworks übertragen. Die drei beliebtesten Testläufer sind:
-
unittest
-
nose
odernose2
-
pytest
Es ist wichtig, den besten Testläufer für Ihre Anforderungen und Ihre Erfahrung auszuwählen.
unittest
unittest
wurde seit Version 2.1 in die Python-Standardbibliothek integriert. Sie werden es wahrscheinlich in kommerziellen Python-Anwendungen und Open-Source-Projekten sehen.
unittest
enthält sowohl ein Testframework als auch einen Testläufer. unittest
hat einige wichtige Anforderungen zum Schreiben und Ausführen von Tests.
unittest
erfordert Folgendes:
-
Sie ordnen Ihre Tests als Methoden in Klassen ein
-
Sie verwenden eine Reihe spezieller Assertionsmethoden in der Klasse
unittest.TestCase
anstelle der integrierten Anweisungassert
Um das vorherige Beispiel in einen Testfall vonunittest
zu konvertieren, müssten Sie:
-
Importieren Sie
unittest
aus der Standardbibliothek -
Erstellen Sie eine Klasse mit dem Namen
TestSum
, die von der KlasseTestCase
erbt -
Konvertieren Sie die Testfunktionen in Methoden, indem Sie als erstes Argument
self
hinzufügen -
Ändern Sie die Zusicherungen, um die Methode
self.assertEqual()
für die KlasseTestCase
zu verwenden -
Ändern Sie den Befehlszeilen-Einstiegspunkt in
unittest.main()
Befolgen Sie diese Schritte, indem Sie eine neue Dateitest_sum_unittest.py
mit dem folgenden Code erstellen:
import unittest
class TestSum(unittest.TestCase):
def test_sum(self):
self.assertEqual(sum([1, 2, 3]), 6, "Should be 6")
def test_sum_tuple(self):
self.assertEqual(sum((1, 2, 2)), 6, "Should be 6")
if __name__ == '__main__':
unittest.main()
Wenn Sie dies in der Befehlszeile ausführen, sehen Sie einen Erfolg (angezeigt mit.
) und einen Fehler (angezeigt mitF
):
$ python test_sum_unittest.py
.F
======================================================================
FAIL: test_sum_tuple (__main__.TestSum)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_sum_unittest.py", line 9, in test_sum_tuple
self.assertEqual(sum((1, 2, 2)), 6, "Should be 6")
AssertionError: Should be 6
----------------------------------------------------------------------
Ran 2 tests in 0.001s
FAILED (failures=1)
Sie haben gerade zwei Tests mit dem Testläuferunittest
ausgeführt.
Note: Seien Sie vorsichtig, wenn Sie Testfälle schreiben, die sowohl in Python 2 als auch in Python 3 ausgeführt werden müssen. In Python 2.7 und niedriger heißtunittest
unittest2
. Wenn Sie einfach ausunittest
importieren, erhalten Sie verschiedene Versionen mit unterschiedlichen Funktionen zwischen Python 2 und 3.
Weitere Informationen zuunittest
finden Sie unterunittest Documentation.
nose
Wenn Sie Hunderte oder sogar Tausende von Tests für Ihre Anwendung schreiben, wird es im Laufe der Zeit möglicherweise schwieriger, die Ausgabe vonunittest
zu verstehen und zu verwenden.
nose
ist mit allen Tests kompatibel, die mit demunittest
-Framework geschrieben wurden, und kann als Ersatz für denunittest
-Testläufer verwendet werden. Die Entwicklung vonnose
als Open-Source-Anwendung blieb zurück, und es wurde eine Abzweigung namensnose2
erstellt. Wenn Sie bei Null anfangen, wird empfohlen,nose2
anstelle vonnose
zu verwenden.
Um mitnose2
zu beginnen, installieren Sienose2
von PyPI und führen Sie es in der Befehlszeile aus. nose2
versucht, alle Testskripte mit dem Namentest*.py
und Testfälle zu ermitteln, die vonunittest.TestCase
in Ihrem aktuellen Verzeichnis erben:
$ pip install nose2
$ python -m nose2
.F
======================================================================
FAIL: test_sum_tuple (__main__.TestSum)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_sum_unittest.py", line 9, in test_sum_tuple
self.assertEqual(sum((1, 2, 2)), 6, "Should be 6")
AssertionError: Should be 6
----------------------------------------------------------------------
Ran 2 tests in 0.001s
FAILED (failures=1)
Sie haben gerade den Test ausgeführt, den Sie intest_sum_unittest.py
vom Testläufernose2
erstellt haben. nose2
bietet viele Befehlszeilenflags zum Filtern der von Ihnen ausgeführten Tests. Weitere Informationen finden Sie unterNose 2 documentation.
pytest
pytest
unterstützt die Ausführung vonunittest
Testfällen. Der eigentliche Vorteil vonpytest
besteht darin,pytest
Testfälle zu schreiben. pytest
Testfälle sind eine Reihe von Funktionen in einer Python-Datei, die mit dem Namentest_
beginnen.
pytest
hat einige andere großartige Funktionen:
-
Unterstützung für die integrierte
assert
-Anweisung anstelle der Verwendung speziellerself.assert*()
-Methoden -
Unterstützung für das Filtern von Testfällen
-
Möglichkeit, den letzten fehlgeschlagenen Test erneut auszuführen
-
Ein Ökosystem mit Hunderten von Plugins zur Erweiterung der Funktionalität
Das Schreiben des Testfallbeispiels fürTestSum
fürpytest
würde folgendermaßen aussehen:
def test_sum():
assert sum([1, 2, 3]) == 6, "Should be 6"
def test_sum_tuple():
assert sum((1, 2, 2)) == 6, "Should be 6"
Sie habenTestCase
, die Verwendung von Klassen und den Befehlszeilen-Einstiegspunkt gelöscht.
Weitere Informationen finden Sie unterPytest Documentation Website.
Schreiben Sie Ihren ersten Test
Lassen Sie uns zusammenfassen, was Sie bisher gelernt haben, und anstatt die integriertesum()
-Funktion zu testen, eine einfache Implementierung derselben Anforderung testen.
Erstellen Sie einen neuen Projektordner und erstellen Sie darin einen neuen Ordner mit dem Namenmy_sum
. Erstellen Sie inmy_sum
eine leere Datei mit dem Namen__init__.py
. Durch das Erstellen der Datei__init__.py
kann der Ordnermy_sum
als Modul aus dem übergeordneten Verzeichnis importiert werden.
Ihr Projektordner sollte folgendermaßen aussehen:
project/ │ └── my_sum/ └── __init__.py
Öffnen Siemy_sum/__init__.py
und erstellen Sie eine neue Funktion namenssum()
, die eine Iterable (eine Liste, ein Tupel oder eine Menge) verwendet und die Werte addiert:
def sum(arg):
total = 0
for val in arg:
total += val
return total
In diesem Codebeispiel wird eine Variable mit dem Namentotal
erstellt, alle Werte inarg
durchlaufen und zutotal
hinzugefügt. Es gibt dann das Ergebnis zurück, sobald das iterable erschöpft ist.
Wo schreibe ich den Test?
Um mit dem Schreiben von Tests zu beginnen, können Sie einfach eine Datei mit dem Namentest.py
erstellen, die Ihren ersten Testfall enthält. Da die Datei Ihre Anwendung importieren muss, um sie testen zu können, möchten Sietest.py
über dem Paketordner platzieren, damit Ihr Verzeichnisbaum ungefähr so aussieht:
project/ │ ├── my_sum/ │ └── __init__.py | └── test.py
Wenn Sie mehr und mehr Tests hinzufügen, wird Ihre einzelne Datei unübersichtlich und schwer zu warten. Sie können also einen Ordner mit dem Namentests/
erstellen und die Tests in mehrere Dateien aufteilen. Es ist üblich, sicherzustellen, dass jede Datei mittest_
beginnt, damit alle Testläufer davon ausgehen, dass die Python-Datei auszuführende Tests enthält. Einige sehr große Projekte teilen Tests je nach Zweck oder Verwendung in mehrere Unterverzeichnisse auf.
Note: Was ist, wenn Ihre Anwendung ein einzelnes Skript ist?
Sie können alle Attribute des Skripts wie Klassen, Funktionen und Variablen mithilfe der integrierten Funktion__import__()
importieren. Anstelle vonfrom my_sum import sum
können Sie Folgendes schreiben:
target = __import__("my_sum.py")
sum = target.sum
Die Verwendung von__import__()
hat den Vorteil, dass Sie Ihren Projektordner nicht in ein Paket verwandeln müssen und den Dateinamen angeben können. Dies ist auch nützlich, wenn Ihr Dateiname mit Standardbibliothekspaketen kollidiert. Zum Beispiel würdemath.py
mit dem Modulmath
kollidieren.
So strukturieren Sie einen einfachen Test
Bevor Sie mit dem Schreiben von Tests beginnen, sollten Sie zunächst einige Entscheidungen treffen:
-
Was möchten Sie testen?
-
Schreiben Sie einen Unit-Test oder einen Integrationstest?
Dann sollte die Struktur eines Tests diesem Workflow lose folgen:
-
Erstellen Sie Ihre Eingaben
-
Führen Sie den zu testenden Code aus und erfassen Sie die Ausgabe
-
Vergleichen Sie die Ausgabe mit einem erwarteten Ergebnis
Für diese Anwendung testen Siesum()
. Es gibt viele Verhaltensweisen insum()
, die Sie überprüfen können, z.
-
Kann es eine Liste ganzer Zahlen (ganze Zahlen) summieren?
-
Kann es ein Tupel oder einen Satz summieren?
-
Kann es eine Liste von Floats zusammenfassen?
-
Was passiert, wenn Sie einen schlechten Wert angeben, z. B. eine einzelne Ganzzahl oder eine Zeichenfolge?
-
Was passiert, wenn einer der Werte negativ ist?
Der einfachste Test wäre eine Liste von ganzen Zahlen. Erstellen Sie eine Dateitest.py
mit dem folgenden Python-Code:
import unittest
from my_sum import sum
class TestSum(unittest.TestCase):
def test_list_int(self):
"""
Test that it can sum a list of integers
"""
data = [1, 2, 3]
result = sum(data)
self.assertEqual(result, 6)
if __name__ == '__main__':
unittest.main()
Dieses Codebeispiel:
-
Importiert
sum()
aus dem von Ihnen erstelltenmy_sum
-Paket -
Definiert eine neue Testfallklasse namens
TestSum
, die vonunittest.TestCase
erbt -
Definiert eine Testmethode,
.test_list_int()
, um eine Liste von Ganzzahlen zu testen. Die Methode.test_list_int()
wird:-
Deklarieren Sie eine Variable
data
mit einer Liste von Zahlen(1, 2, 3)
-
Ordnen Sie das Ergebnis von
my_sum.sum(data)
einer Variablen vonresult
zu -
Stellen Sie sicher, dass der Wert von
result
gleich6
ist, indem Sie die Methode.assertEqual()
für die Klasseunittest.TestCase
verwenden
-
-
Definiert einen Befehlszeilen-Einstiegspunkt, an dem der Testläufer
.main()
vonunittest
ausgeführt wird
Wenn Sie sich nicht sicher sind, wasself
ist oder wie.assertEqual()
definiert ist, können Sie Ihre objektorientierte Programmierung mitPython 3 Object-Oriented Programming auffrischen.
Wie schreibe ich Behauptungen?
Der letzte Schritt beim Schreiben eines Tests besteht darin, die Ausgabe anhand einer bekannten Antwort zu validieren. Dies ist alsassertion bekannt. Es gibt einige allgemeine Best Practices zum Schreiben von Aussagen:
-
Stellen Sie sicher, dass die Tests wiederholbar sind, und führen Sie den Test mehrmals aus, um sicherzustellen, dass jedes Mal das gleiche Ergebnis erzielt wird
-
Versuchen Sie, Ergebnisse zu bestätigen, die sich auf Ihre Eingabedaten beziehen, z. B. indem Sie überprüfen, ob das Ergebnis die tatsächliche Summe der Werte im Beispiel von
sum()
ist
unittest
enthält viele Methoden, um die Werte, Typen und das Vorhandensein von Variablen zu bestätigen. Hier sind einige der am häufigsten verwendeten Methoden:
Methode | Gleichwertig |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.assertIs()
,.assertIsNone()
,.assertIn()
und.assertIsInstance()
haben alle entgegengesetzte Methoden, die als.assertIsNot()
bezeichnet werden, und so weiter.
Nebenwirkungen
Wenn Sie Tests schreiben, ist es oft nicht so einfach, den Rückgabewert einer Funktion zu betrachten. Durch das Ausführen eines Codeteils werden häufig andere Dinge in der Umgebung geändert, z. B. das Attribut einer Klasse, eine Datei im Dateisystem oder ein Wert in einer Datenbank. Diese werden alsside effects bezeichnet und sind ein wichtiger Bestandteil des Testens. Entscheiden Sie, ob die Nebenwirkung getestet wird, bevor Sie sie in Ihre Liste der Behauptungen aufnehmen.
Wenn Sie feststellen, dass die zu testende Codeeinheit viele Nebenwirkungen hat, brechen Sie möglicherweise dieSingle Responsibility Principle. Das Prinzip der Einzelverantwortung zu brechen bedeutet, dass der Code zu viele Dinge tut und besser dran ist, überarbeitet zu werden. Das Befolgen des Einzelverantwortungsprinzips ist eine großartige Möglichkeit, Code zu entwerfen, mit dem sich wiederholbare und einfache Komponententests für und letztendlich zuverlässige Anwendungen leicht schreiben lassen.
Ausführen Ihres ersten Tests
Nachdem Sie den ersten Test erstellt haben, möchten Sie ihn ausführen. Sicher, Sie wissen, dass es erfolgreich sein wird, aber bevor Sie komplexere Tests erstellen, sollten Sie überprüfen, ob Sie die Tests erfolgreich ausführen können.
Testläufer ausführen
Die Python-Anwendung, die Ihren Testcode ausführt, die Zusicherungen überprüft und Ihnen Testergebnisse in Ihrer Konsole liefert, heißttest runner.
Am Ende vontest.py
haben Sie diesen kleinen Codeausschnitt hinzugefügt:
if __name__ == '__main__':
unittest.main()
Dies ist ein Befehlszeilen-Einstiegspunkt. Wenn Sie das Skript alleine ausführen, indem Siepython test.py
in der Befehlszeile ausführen, wirdunittest.main()
aufgerufen. Dadurch wird der Testläufer ausgeführt, indem alle Klassen in dieser Datei ermittelt werden, die vonunittest.TestCase
erben.
Dies ist eine von vielen Möglichkeiten, den Testläufer vonunittest
auszuführen. Wenn Sie eine einzelne Testdatei mit dem Namentest.py
haben, ist der Aufruf vonpython test.py
eine gute Möglichkeit, um loszulegen.
Eine andere Möglichkeit ist die Verwendung der Befehlszeile vonunittest
. Versuche dies:
$ python -m unittest test
Dadurch wird dasselbe Testmodul (test
genannt) über die Befehlszeile ausgeführt.
Sie können zusätzliche Optionen zum Ändern der Ausgabe bereitstellen. Eine davon ist-v
für ausführlich. Versuchen Sie das als nächstes:
$ python -m unittest -v test
test_list_int (test.TestSum) ... ok
----------------------------------------------------------------------
Ran 1 tests in 0.000s
Dies führte den einen Test innerhalb vontest.py
aus und druckte die Ergebnisse auf die Konsole. Im ausführlichen Modus werden die Namen der Tests aufgeführt, die zuerst ausgeführt wurden, sowie das Ergebnis jedes Tests.
Anstatt den Namen eines Moduls anzugeben, das Tests enthält, können Sie eine automatische Erkennung wie folgt anfordern:
$ python -m unittest discover
Dadurch wird das aktuelle Verzeichnis nach Dateien mit dem Namentest*.py
durchsucht und versucht, diese zu testen.
Sobald Sie mehrere Testdateien haben, können Sie, solange Sie dem Namensmuster vontest*.py
folgen, stattdessen den Namen des Verzeichnisses angeben, indem Sie das Flag-s
und den Namen des Verzeichnisses verwenden:
$ python -m unittest discover -s tests
unittest
führt alle Tests in einem einzigen Testplan aus und gibt Ihnen die Ergebnisse.
Wenn sich Ihr Quellcode nicht im Verzeichnisstamm befindet und in einem Unterverzeichnis enthalten ist, z. B. in einem Ordner namenssrc/
, können Sieunittest
mitteilen, wo die Tests ausgeführt werden sollen, damit die Module importiert werden können richtig mit dem-t
Flag:
$ python -m unittest discover -s tests -t src
unittest
wechselt in das Verzeichnissrc/
, sucht nach allentest*.py
-Dateien im Verzeichnistests
und führt sie aus.
Grundlegendes zur Testausgabe
Dies war ein sehr einfaches Beispiel, bei dem alles erfolgreich war. Jetzt werden Sie einen fehlgeschlagenen Test versuchen und die Ausgabe interpretieren.
sum()
sollten in der Lage sein, andere Listen numerischer Typen wie Brüche zu akzeptieren.
Fügen Sie oben in der Dateitest.py
eine Importanweisung hinzu, um den TypFraction
aus dem Modulfractions
in der Standardbibliothek zu importieren:
from fractions import Fraction
Fügen Sie nun einen Test mit einer Behauptung hinzu, die den falschen Wert erwartet. In diesem Fall wird erwartet, dass die Summe von 1/4, 1/4 und 2/5 1 ist:
import unittest
from my_sum import sum
class TestSum(unittest.TestCase):
def test_list_int(self):
"""
Test that it can sum a list of integers
"""
data = [1, 2, 3]
result = sum(data)
self.assertEqual(result, 6)
def test_list_fraction(self):
"""
Test that it can sum a list of fractions
"""
data = [Fraction(1, 4), Fraction(1, 4), Fraction(2, 5)]
result = sum(data)
self.assertEqual(result, 1)
if __name__ == '__main__':
unittest.main()
Wenn Sie die Tests erneut mitpython -m unittest test
ausführen, sollte die folgende Ausgabe angezeigt werden:
$ python -m unittest test
F.
======================================================================
FAIL: test_list_fraction (test.TestSum)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test.py", line 21, in test_list_fraction
self.assertEqual(result, 1)
AssertionError: Fraction(9, 10) != 1
----------------------------------------------------------------------
Ran 2 tests in 0.001s
FAILED (failures=1)
In der Ausgabe werden die folgenden Informationen angezeigt:
-
Die erste Zeile zeigt die Ausführungsergebnisse aller Tests, von denen einer fehlgeschlagen (
F
) und einer bestanden (.
) ist. -
Der Eintrag
FAIL
zeigt einige Details zum fehlgeschlagenen Test:-
Der Name der Testmethode (
test_list_fraction
) -
Das Testmodul (
test
) und der Testfall (TestSum
) -
Ein Traceback zur fehlerhaften Zeile
-
Die Details der Behauptung mit dem erwarteten Ergebnis (
1
) und dem tatsächlichen Ergebnis (Fraction(9, 10)
)
-
Denken Sie daran, dass Sie der Testausgabe zusätzliche Informationen hinzufügen können, indem Sie dem Befehlpython -m unittest
das Flag-v
hinzufügen.
Ausführen Ihrer Tests über PyCharm
Wenn Sie die PyCharm-IDE verwenden, können Sieunittest
oderpytest
folgendermaßen ausführen:
-
Wählen Sie im Fenster des Projektwerkzeugs das Verzeichnis
tests
aus. -
Wählen Sie im Kontextmenü den Befehl run für
unittest
. Wählen Sie beispielsweiseRun ‘Unittests in my Tests…’.
Dadurch werdenunittest
in einem Testfenster ausgeführt und Sie erhalten die Ergebnisse in PyCharm:
Weitere Informationen finden Sie unterPyCharm Website.
Ausführen Ihrer Tests über Visual Studio-Code
Wenn Sie die Microsoft Visual Studio-Code-IDE verwenden, ist die Ausführung vonunittest
,nose
undpytest
in das Python-Plugin integriert.
Wenn Sie das Python-Plugin installiert haben, können Sie die Konfiguration Ihrer Tests einrichten, indem Sie die Befehlspalette mitCtrl[.kbd .key-shift]##Shift##[.kbd .key-p]#P # öffnen und "Python-Test" eingeben. Sie sehen eine Reihe von Optionen:
Wählen SieDebug All Unit Tests, und VSCode fordert Sie auf, das Testframework zu konfigurieren. Klicken Sie auf das Zahnrad, um den Testläufer (unittest
) und das Ausgangsverzeichnis (.
) auszuwählen.
Sobald dies eingerichtet ist, sehen Sie den Status Ihrer Tests am unteren Rand des Fensters. Sie können schnell auf die Testprotokolle zugreifen und die Tests erneut ausführen, indem Sie auf diese Symbole klicken:
Dies zeigt, dass die Tests ausgeführt werden, einige jedoch fehlschlagen.
Testen auf Web Frameworks wie Django und Flask
Wenn Sie Tests für eine Webanwendung mit einem der gängigen Frameworks wie Django oder Flask schreiben, gibt es einige wichtige Unterschiede in der Art und Weise, wie Sie die Tests schreiben und ausführen.
Warum sie sich von anderen Anwendungen unterscheiden
Denken Sie an den gesamten Code, den Sie in einer Webanwendung testen möchten. Die Routen, Ansichten und Modelle erfordern viele Importe und Kenntnisse über die verwendeten Frameworks.
Dies ähnelt dem Autotest zu Beginn des Tutorials: Sie müssen den Computer des Autos starten, bevor Sie einen einfachen Test wie das Überprüfen der Lichter durchführen können.
Django und Flask machen dies für Sie einfach, indem sie ein Test-Framework bereitstellen, das aufunittest
basiert. Sie können weiterhin Tests so schreiben, wie Sie es gelernt haben, sie jedoch etwas anders ausführen.
Verwendung des Django Test Runner
Die Vorlage von Djangostartapp
hat einetests.py
-Datei in Ihrem Anwendungsverzeichnis erstellt. Wenn Sie das noch nicht haben, können Sie es mit den folgenden Inhalten erstellen:
from django.test import TestCase
class MyTestCase(TestCase):
# Your test methods
Der Hauptunterschied zu den bisherigen Beispielen besteht darin, dass Sie vondjango.test.TestCase
anstelle vonunittest.TestCase
erben müssen. Diese Klassen haben dieselbe API, aber die Klasse von DjangoTestCase
richtet den gesamten zu testenden Status ein.
Um Ihre Testsuite auszuführen, verwenden Sie anstelle vonunittest
in der Befehlszeilemanage.py test
:
$ python manage.py test
Wenn Sie mehrere Testdateien möchten, ersetzen Sietests.py
durch einen Ordner mit dem Namentests
, fügen Sie eine leere Datei mit dem Namen__init__.py
ein und erstellen Sie Ihretest_*.py
-Dateien. Django wird diese entdecken und ausführen.
Weitere Informationen finden Sie unterDjango Documentation Website.
Verwendung vonunittest
und Kolben
Flask erfordert, dass die App importiert und dann in den Testmodus versetzt wird. Sie können einen Testclient instanziieren und mit dem Testclient Anforderungen an beliebige Routen in Ihrer Anwendung stellen.
Die gesamte Instanziierung des Testclients erfolgt nach dersetUp
-Methode Ihres Testfalls. Im folgenden Beispiel istmy_app
der Name der Anwendung. Machen Sie sich keine Sorgen, wenn Sie nicht wissen, wassetUp
tut. Weitere Informationen hierzu finden Sie im AbschnittMore Advanced Testing Scenarios.
Der Code in Ihrer Testdatei sollte folgendermaßen aussehen:
import my_app
import unittest
class MyTestCase(unittest.TestCase):
def setUp(self):
my_app.app.testing = True
self.app = my_app.app.test_client()
def test_home(self):
result = self.app.get('/')
# Make your assertions
Anschließend können Sie die Testfälle mit dem Befehlpython -m unittest discover
ausführen.
Weitere Informationen finden Sie unterFlask Documentation Website.
Erweiterte Testszenarien
Beachten Sie die drei grundlegenden Schritte jedes Tests, bevor Sie Tests für Ihre Anwendung erstellen:
-
Erstellen Sie Ihre Eingaben
-
Führen Sie den Code aus und erfassen Sie die Ausgabe
-
Vergleichen Sie die Ausgabe mit einem erwarteten Ergebnis
Es ist nicht immer so einfach, einen statischen Wert für die Eingabe wie eine Zeichenfolge oder eine Zahl zu erstellen. Manchmal erfordert Ihre Anwendung eine Instanz einer Klasse oder eines Kontexts. Was machst du dann?
Die Daten, die Sie als Eingabe erstellen, werden alsfixture bezeichnet. Es ist üblich, Geräte zu erstellen und wiederzuverwenden.
Wenn Sie denselben Test ausführen und jedes Mal andere Werte bestehen und dasselbe Ergebnis erwarten, wird dies alsparameterization bezeichnet.
Umgang mit erwarteten Fehlern
Als Sie zuvor eine Liste von Szenarien zum Testen vonsum()
erstellt haben, wurde die Frage gestellt: Was passiert, wenn Sie einen schlechten Wert angeben, z. B. eine einzelne Ganzzahl oder eine Zeichenfolge?
In diesem Fall würden Sie erwarten, dasssum()
einen Fehler auslöst. Wenn ein Fehler ausgegeben wird, schlägt der Test fehl.
Es gibt eine spezielle Möglichkeit, mit erwarteten Fehlern umzugehen. Sie können.assertRaises()
als Kontextmanager verwenden und dann im Blockwith
die folgenden Testschritte ausführen:
import unittest
from my_sum import sum
class TestSum(unittest.TestCase):
def test_list_int(self):
"""
Test that it can sum a list of integers
"""
data = [1, 2, 3]
result = sum(data)
self.assertEqual(result, 6)
def test_list_fraction(self):
"""
Test that it can sum a list of fractions
"""
data = [Fraction(1, 4), Fraction(1, 4), Fraction(2, 5)]
result = sum(data)
self.assertEqual(result, 1)
def test_bad_type(self):
data = "banana"
with self.assertRaises(TypeError):
result = sum(data)
if __name__ == '__main__':
unittest.main()
Dieser Testfall besteht nur, wennsum(data)
TypeError
erhöht. Sie könnenTypeError
durch einen beliebigen Ausnahmetyp ersetzen.
Verhalten in Ihrer Anwendung isolieren
Zu Beginn des Tutorials haben Sie gelernt, was eine Nebenwirkung ist. Nebenwirkungen erschweren das Testen von Einheiten, da jedes Mal, wenn ein Test ausgeführt wird, ein anderes Ergebnis erzielt werden kann, oder noch schlimmer, ein Test kann sich auf den Status der Anwendung auswirken und dazu führen, dass ein anderer Test fehlschlägt!
Es gibt einige einfache Techniken, mit denen Sie Teile Ihrer Anwendung testen können, die viele Nebenwirkungen haben:
-
Refactoring-Code nach dem Prinzip der Einzelverantwortung
-
Verspotten von Methoden- oder Funktionsaufrufen, um Nebenwirkungen zu beseitigen
-
Verwenden von Integrationstests anstelle von Komponententests für diesen Teil der Anwendung
Wenn Sie mit Verspotten nicht vertraut sind, finden Sie unterPython CLI Testingeinige gute Beispiele.
Integrationstests schreiben
Bisher haben Sie hauptsächlich über Unit-Tests gelernt. Unit-Tests sind eine großartige Möglichkeit, vorhersehbaren und stabilen Code zu erstellen. Aber am Ende des Tages muss Ihre Anwendung funktionieren, wenn sie startet!
Beim Integrationstest werden mehrere Komponenten der Anwendung getestet, um zu überprüfen, ob sie zusammenarbeiten. Integrationstests erfordern möglicherweise das Verhalten eines Verbrauchers oder Benutzers der Anwendung durch:
-
Aufrufen einer HTTP-REST-API
-
Aufruf einer Python-API
-
Aufrufen eines Webdienstes
-
Ausführen einer Befehlszeile
Jeder dieser Arten von Integrationstests kann auf die gleiche Weise wie ein Komponententest nach dem Muster Eingabe, Ausführen und Bestätigen geschrieben werden. Der wichtigste Unterschied besteht darin, dass Integrationstests mehr Komponenten gleichzeitig prüfen und daher mehr Nebenwirkungen haben als ein Komponententest. Für Integrationstests müssen außerdem mehr Geräte vorhanden sein, z. B. eine Datenbank, ein Netzwerk-Socket oder eine Konfigurationsdatei.
Aus diesem Grund empfiehlt es sich, Ihre Komponententests und Ihre Integrationstests zu trennen. Die Erstellung von Fixtures, die für eine Integration wie eine Testdatenbank und die Testfälle selbst erforderlich sind, dauert häufig viel länger als Unit-Tests. Daher möchten Sie Integrationstests möglicherweise nur ausführen, bevor Sie zur Produktion wechseln, anstatt bei jedem Commit einmal.
Eine einfache Möglichkeit, Unit- und Integrationstests zu trennen, besteht darin, sie in verschiedenen Ordnern abzulegen:
project/ │ ├── my_app/ │ └── __init__.py │ └── tests/ | ├── unit/ | ├── __init__.py | └── test_sum.py | └── integration/ ├── __init__.py └── test_integration.py
Es gibt viele Möglichkeiten, nur eine ausgewählte Gruppe von Tests auszuführen. Das angegebene Quellverzeichnis-Flag-s
kann zuunittest discover
hinzugefügt werden, wobei der Pfad die Tests enthält:
$ python -m unittest discover -s tests/integration
unittest
hat Ihnen die Ergebnisse aller Tests im Verzeichnistests/integration
angezeigt.
Testen datengesteuerter Anwendungen
Für viele Integrationstests müssen Backend-Daten wie eine Datenbank mit bestimmten Werten vorhanden sein. Beispielsweise möchten Sie möglicherweise einen Test durchführen, der überprüft, ob die Anwendung bei mehr als 100 Kunden in der Datenbank korrekt angezeigt wird, oder die Bestellseite funktioniert auch dann, wenn die Produktnamen auf Japanisch angezeigt werden.
Diese Arten von Integrationstests hängen von verschiedenen Testvorrichtungen ab, um sicherzustellen, dass sie wiederholbar und vorhersehbar sind.
Eine gute Methode besteht darin, die Testdaten in einem Ordner in Ihrem Integrationstestordner mit dem Namenfixtures
zu speichern, um anzuzeigen, dass sie Testdaten enthalten. Anschließend können Sie innerhalb Ihrer Tests die Daten laden und den Test ausführen.
Hier ist ein Beispiel für diese Struktur, wenn die Daten aus JSON-Dateien bestanden:
project/ │ ├── my_app/ │ └── __init__.py │ └── tests/ | └── unit/ | ├── __init__.py | └── test_sum.py | └── integration/ | ├── fixtures/ | ├── test_basic.json | └── test_complex.json | ├── __init__.py └── test_integration.py
In Ihrem Testfall können Sie die Methode.setUp()
verwenden, um die Testdaten aus einer Fixture-Datei in einem bekannten Pfad zu laden und viele Tests für diese Testdaten auszuführen. Denken Sie daran, dass Sie mehrere Testfälle in einer einzigen Python-Datei haben können und die Erkennung vonunittest
beide ausführt. Sie können einen Testfall für jeden Satz von Testdaten haben:
import unittest
class TestBasic(unittest.TestCase):
def setUp(self):
# Load test data
self.app = App(database='fixtures/test_basic.json')
def test_customer_count(self):
self.assertEqual(len(self.app.customers), 100)
def test_existence_of_customer(self):
customer = self.app.get_customer(id=10)
self.assertEqual(customer.name, "Org XYZ")
self.assertEqual(customer.address, "10 Red Road, Reading")
class TestComplexData(unittest.TestCase):
def setUp(self):
# load test data
self.app = App(database='fixtures/test_complex.json')
def test_customer_count(self):
self.assertEqual(len(self.app.customers), 10000)
def test_existence_of_customer(self):
customer = self.app.get_customer(id=9999)
self.assertEqual(customer.name, u"バナナ")
self.assertEqual(customer.address, "10 Red Road, Akihabara, Tokyo")
if __name__ == '__main__':
unittest.main()
Wenn Ihre Anwendung von Daten von einem Remotestandort abhängt, z. B. von einer Remote-API, möchten Sie sicherstellen, dass Ihre Tests wiederholbar sind. Wenn Ihre Tests fehlschlagen, weil die API offline ist oder ein Konnektivitätsproblem vorliegt, kann dies die Entwicklung verlangsamen. In solchen Situationen empfiehlt es sich, Remote-Geräte lokal zu speichern, damit sie abgerufen und an die Anwendung gesendet werden können.
Dierequests
-Bibliothek enthält ein kostenloses Paket namensresponses
, mit dem Sie Antwort-Fixtures erstellen und in Ihren Testordnern speichern können. Erfahren Sie mehron their GitHub Page.
Testen in mehreren Umgebungen
Bisher haben Sie eine einzelne Version von Python in einer virtuellen Umgebung mit bestimmten Abhängigkeiten getestet. Möglicherweise möchten Sie überprüfen, ob Ihre Anwendung mit mehreren Versionen von Python oder mehreren Versionen eines Pakets funktioniert. Tox ist eine Anwendung, die das Testen in mehreren Umgebungen automatisiert.
Tox installieren
Tox ist auf PyPI als Paket verfügbar, das überpip
installiert werden kann:
$ pip install tox
Nachdem Sie Tox installiert haben, muss es konfiguriert werden.
Konfigurieren von Tox für Ihre Abhängigkeiten
Tox wird über eine Konfigurationsdatei in Ihrem Projektverzeichnis konfiguriert. Die Tox-Konfigurationsdatei enthält Folgendes:
-
Der Befehl, der ausgeführt werden soll, um Tests auszuführen
-
Alle zusätzlichen Pakete, die vor der Ausführung erforderlich sind
-
Die Ziel-Python-Versionen, gegen die getestet werden soll
Anstatt die Tox-Konfigurationssyntax lernen zu müssen, können Sie sich einen Vorsprung verschaffen, indem Sie die Schnellstartanwendung ausführen:
$ tox-quickstart
Das Tox-Konfigurationstool stellt Ihnen diese Fragen und erstellt eine Datei ähnlich der folgenden intox.ini
:
[tox]
envlist = py27, py36
[testenv]
deps =
commands =
python -m unittest discover
Bevor Sie Tox ausführen können, muss sich in Ihrem Anwendungsordner einesetup.py
-Datei befinden, die die Schritte zum Installieren Ihres Pakets enthält. Wenn Sie noch keine haben, können Siethis guide folgen, umsetup.py
zu erstellen, bevor Sie fortfahren.
Wenn Ihr Projekt nicht für die Verteilung auf PyPI vorgesehen ist, können Sie diese Anforderung überspringen, indem Sie die folgende Zeile in die Dateitox.ini
unter der Überschrift[tox]
einfügen:
[tox]
envlist = py27, py36
skipsdist=True
Wenn Sie keinesetup.py
erstellen und Ihre Anwendung einige Abhängigkeiten von PyPI aufweist, müssen Sie diese in mehreren Zeilen im Abschnitt[testenv]
angeben. Zum Beispiel würde Django Folgendes erfordern:
[testenv]
deps = django
Sobald Sie diese Phase abgeschlossen haben, können Sie die Tests ausführen.
Sie können Tox jetzt ausführen und es werden zwei virtuelle Umgebungen erstellt: eine für Python 2.7 und eine für Python 3.6. Das Tox-Verzeichnis heißt.tox/
. Im Verzeichnis.tox/
führt Toxpython -m unittest discover
für jede virtuelle Umgebung aus.
Sie können diesen Prozess ausführen, indem Sie Tox über die Befehlszeile aufrufen:
$ tox
Tox gibt die Ergebnisse Ihrer Tests für jede Umgebung aus. Beim ersten Ausführen benötigt Tox etwas Zeit, um die virtuellen Umgebungen zu erstellen. Sobald dies der Fall ist, ist die zweite Ausführung viel schneller.
Tox ausführen
Die Ausgabe von Tox ist recht einfach. Es erstellt eine Umgebung für jede Version, installiert Ihre Abhängigkeiten und führt dann die Testbefehle aus.
Es gibt einige zusätzliche Befehlszeilenoptionen, die Sie sich gut merken sollten.
Führen Sie nur eine einzige Umgebung aus, z. B. Python 3.6:
$ tox -e py36
Erstellen Sie die virtuellen Umgebungen neu, falls sich Ihre Abhängigkeiten geändert haben odersite-packages beschädigt ist:
$ tox -r
Führen Sie Tox mit weniger ausführlicher Ausgabe aus:
$ tox -q
Ausführen von Tox mit ausführlicherer Ausgabe:
$ tox -v
Weitere Informationen zu Tox finden Sie unterTox Documentation Website.
Automatisieren Sie die Ausführung Ihrer Tests
Bisher haben Sie die Tests manuell ausgeführt, indem Sie einen Befehl ausgeführt haben. Es gibt einige Tools zum automatischen Ausführen von Tests, wenn Sie Änderungen vornehmen und diese in ein Quellcodeverwaltungs-Repository wie Git übertragen. Automatisierte Testtools werden häufig als CI / CD-Tools bezeichnet, was für "Continuous Integration / Continuous Deployment" steht. Sie können Ihre Tests ausführen, Anwendungen kompilieren, veröffentlichen und sogar in der Produktion bereitstellen.
Travis CI ist einer von vielen verfügbaren CI-Diensten (Continuous Integration).
Travis CI funktioniert gut mit Python. Nachdem Sie alle diese Tests erstellt haben, können Sie die Ausführung in der Cloud automatisieren. Travis CI ist für Open-Source-Projekte auf GitHub und GitLab kostenlos und für private Projekte kostenpflichtig.
Melden Sie sich zunächst auf der Website an und authentifizieren Sie sich mit Ihren GitHub- oder GitLab-Anmeldeinformationen. Erstellen Sie dann eine Datei mit dem Namen.travis.yml
mit dem folgenden Inhalt:
language: python
python:
- "2.7"
- "3.7"
install:
- pip install -r requirements.txt
script:
- python -m unittest discover
Diese Konfiguration weist Travis CI an:
-
Test gegen Python 2.7 und 3.7 (Sie können diese Versionen durch eine beliebige ersetzen.)
-
Installieren Sie alle Pakete, die Sie in
requirements.txt
auflisten (Sie sollten diesen Abschnitt entfernen, wenn Sie keine Abhängigkeiten haben.) -
Führen Sie
python -m unittest discover
aus, um die Tests auszuführen
Sobald Sie diese Datei festgeschrieben und gepusht haben, führt Travis CI diese Befehle jedes Mal aus, wenn Sie sie in Ihr Remote-Git-Repository pushen. Sie können die Ergebnisse auf ihrer Website überprüfen.
Was kommt als nächstes
Nachdem Sie nun gelernt haben, wie Sie Tests erstellen, ausführen, in Ihr Projekt aufnehmen und sogar automatisch ausführen, gibt es einige fortgeschrittene Techniken, die Sie möglicherweise nützlich finden, wenn Ihre Testbibliothek wächst.
Einführung von Lintern in Ihre Anwendung
Tox und Travis CI haben eine Konfiguration für einen Testbefehl. Der Testbefehl, den Sie in diesem Lernprogramm verwendet haben, lautetpython -m unittest discover
.
Sie können in all diesen Tools einen oder mehrere Befehle bereitstellen. Mit dieser Option können Sie weitere Tools hinzufügen, die die Qualität Ihrer Anwendung verbessern.
Eine solche Art der Anwendung wird als Linter bezeichnet. Ein Linter wird sich Ihren Code ansehen und ihn kommentieren. Es kann Ihnen Tipps zu Fehlern geben, nachgestellte Leerzeichen korrigieren und sogar Fehler vorhersagen, die Sie möglicherweise eingeführt haben.
Weitere Informationen zu Lintern finden Sie inPython Code Quality tutorial.
Passives Flusen mitflake8
Ein beliebter Linter, der den Stil Ihres Codes in Bezug auf die Spezifikation vonPEP 8kommentiert, istflake8
.
Sie könnenflake8
mitpip
installieren:
$ pip install flake8
Sie können dannflake8
über eine einzelne Datei, einen Ordner oder ein Muster ausführen:
$ flake8 test.py
test.py:6:1: E302 expected 2 blank lines, found 1
test.py:23:1: E305 expected 2 blank lines after class or function definition, found 1
test.py:24:20: W292 no newline at end of file
Sie sehen eine Liste von Fehlern und Warnungen für Ihren Code, dieflake8
gefunden hat.
flake8
kann in der Befehlszeile oder in einer Konfigurationsdatei in Ihrem Projekt konfiguriert werden. Wenn Sie bestimmte Regeln wieE305
oben ignorieren möchten, können Sie sie in der Konfiguration festlegen. flake8
überprüft eine.flake8
-Datei im Projektordner oder einesetup.cfg
-Datei. Wenn Sie sich für Tox entschieden haben, können Sie den Konfigurationsabschnittflake8
intox.ini
einfügen.
In diesem Beispiel werden die Verzeichnisse.git
und__pycache__
sowie die RegelE305
ignoriert. Außerdem wird die maximale Zeilenlänge auf 90 anstatt auf 80 Zeichen festgelegt. Sie werden wahrscheinlich feststellen, dass die Standardbeschränkung von 79 Zeichen für die Zeilenbreite für Tests sehr einschränkend ist, da sie lange Methodennamen, Zeichenfolgenliterale mit Testwerten und andere Daten enthalten, die länger sein können. Es ist üblich, die Zeilenlänge für Tests auf bis zu 120 Zeichen festzulegen:
[flake8]
ignore = E305
exclude = .git,__pycache__
max-line-length = 90
Alternativ können Sie diese Optionen in der Befehlszeile bereitstellen:
$ flake8 --ignore E305 --exclude .git,__pycache__ --max-line-length=90
Eine vollständige Liste der Konfigurationsoptionen finden Sie aufDocumentation Website.
Sie können jetztflake8
zu Ihrer CI-Konfiguration hinzufügen. Für Travis CI würde dies wie folgt aussehen:
matrix:
include:
- python: "2.7"
script: "flake8"
Travis liest die Konfiguration in.flake8
und schlägt die Erstellung fehl, wenn Flusenfehler auftreten. Stellen Sie sicher, dass Sie dieflake8
-abhängigkeit zu Ihrerrequirements.txt
-Datei hinzufügen.
Aggressives Flusen mit einem Code-Formatierer
flake8
ist ein passiver Linter: Es werden Änderungen empfohlen, aber Sie müssen den Code ändern. Ein aggressiverer Ansatz ist ein Code-Formatierer. Code-Formatierer ändern Ihren Code automatisch, um eine Sammlung von Stil- und Layoutpraktiken zu erfüllen.
black
ist ein sehr unversöhnlicher Formatierer. Es gibt keine Konfigurationsoptionen und einen ganz bestimmten Stil. Dies macht es zu einem großartigen Drop-In-Tool für Ihre Testpipeline.
Note:black
erfordert Python 3.6+.
Sie könnenblack
über pip installieren:
$ pip install black
Umblack
in der Befehlszeile auszuführen, geben Sie die Datei oder das Verzeichnis an, die Sie formatieren möchten:
$ black test.py
Halten Sie Ihren Testcode sauber
Wenn Sie Tests schreiben, stellen Sie möglicherweise fest, dass Sie Code viel häufiger kopieren und einfügen als in normalen Anwendungen. Tests können sich manchmal sehr wiederholen, aber das ist keineswegs ein Grund, Ihren Code schlampig und schwer zu pflegen zu lassen.
Im Laufe der Zeit werden Sie vieletechnical debt in Ihrem Testcode entwickeln. Wenn Sie wesentliche Änderungen an Ihrer Anwendung vornehmen, die Änderungen an Ihren Tests erfordern, kann dies aufgrund Ihrer Strukturierung eine umständlichere Aufgabe als erforderlich sein Sie.
Versuchen Sie, beim Schreiben von Tests das Prinzip vonDRYzu befolgen:Don’tRepeatYauer.
Testvorrichtungen und -funktionen sind eine großartige Möglichkeit, Testcode zu erstellen, der einfacher zu warten ist. Auch die Lesbarkeit zählt. Erwägen Sie die Bereitstellung eines Flusenwerkzeugs wieflake8
über Ihrem Testcode:
$ flake8 --max-line-length=120 tests/
Testen auf Leistungseinbußen zwischen Änderungen
Es gibt viele Möglichkeiten, Code in Python zu vergleichen. Die Standardbibliothek enthält das Modultimeit
, mit dem Zeitfunktionen mehrmals ausgeführt und die Verteilung angegeben werden können. In diesem Beispiel wirdtest()
100 Mal undprint()
die Ausgabe ausgeführt:
def test():
# ... your code
if __name__ == '__main__':
import timeit
print(timeit.timeit("test()", setup="from __main__ import test", number=100))
Eine weitere Option, wenn Siepytest
als Testläufer verwenden möchten, ist das Pluginpytest-benchmark
. Dies liefert einepytest
-Vorrichtung, die alsbenchmark
bezeichnet wird. Sie könnenbenchmark()
jedes aufrufbare Objekt übergeben, und das Timing des aufrufbaren Objekts wird in den Ergebnissen vonpytest
protokolliert.
Sie könnenpytest-benchmark
von PyPI mitpip
installieren:
$ pip install pytest-benchmark
Anschließend können Sie einen Test hinzufügen, der das Gerät verwendet und den auszuführenden Callable besteht:
def test_my_function(benchmark):
result = benchmark(test)
Die Ausführung vonpytest
liefert nun Benchmark-Ergebnisse:
Weitere Informationen finden Sie unterDocumentation Website.
Testen auf Sicherheitslücken in Ihrer Anwendung
Ein weiterer Test, den Sie für Ihre Anwendung ausführen möchten, besteht darin, nach häufigen Sicherheitsfehlern oder Sicherheitslücken zu suchen.
Sie könnenbandit
von PyPI mitpip
installieren:
$ pip install bandit
Sie können dann den Namen Ihres Anwendungsmoduls mit dem Flag-r
übergeben und erhalten eine Zusammenfassung:
$ bandit -r my_sum
[main] INFO profile include tests: None
[main] INFO profile exclude tests: None
[main] INFO cli include tests: None
[main] INFO cli exclude tests: None
[main] INFO running on Python 3.5.2
Run started:2018-10-08 00:35:02.669550
Test results:
No issues identified.
Code scanned:
Total lines of code: 5
Total lines skipped (#nosec): 0
Run metrics:
Total issues (by severity):
Undefined: 0.0
Low: 0.0
Medium: 0.0
High: 0.0
Total issues (by confidence):
Undefined: 0.0
Low: 0.0
Medium: 0.0
High: 0.0
Files skipped (0):
Wie beiflake8
sind die Regeln, nach denenbandit
Flags konfiguriert werden können, und wenn Sie welche ignorieren möchten, können Sie Ihrersetup.cfg
-Datei den folgenden Abschnitt mit den folgenden Optionen hinzufügen:
[bandit]
exclude: /test
tests: B101,B102,B301
Weitere Details finden Sie unterGitHub Website.
Fazit
Python hat das Testen zugänglich gemacht, indem die Befehle und Bibliotheken eingebaut wurden, die Sie benötigen, um zu überprüfen, ob Ihre Anwendungen wie geplant funktionieren. Der Einstieg in das Testen in Python muss nicht kompliziert sein: Sie könnenunittest
verwenden und kleine, wartbare Methoden schreiben, um Ihren Code zu validieren.
Wenn Sie mehr über das Testen erfahren und Ihre Anwendung wächst, können Sie in Betracht ziehen, zu einem der anderen Testframeworks wiepytest
zu wechseln und erweiterte Funktionen zu nutzen.
Danke fürs Lesen. Ich hoffe, Sie haben eine fehlerfreie Zukunft mit Python!