Erste Schritte mit dem Testen in Python

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:

  1. Ein Integrationstest überprüft, ob Komponenten in Ihrer Anwendung miteinander arbeiten.

  2. 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 vonunittestund 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 Klasseunittest.TestCaseanstelle der integrierten Anweisungassert

Um das vorherige Beispiel in einen Testfall vonunittestzu konvertieren, müssten Sie:

  1. Importieren Sieunittest aus der Standardbibliothek

  2. Erstellen Sie eine Klasse mit dem NamenTestSum, die von der KlasseTestCase erbt

  3. Konvertieren Sie die Testfunktionen in Methoden, indem Sie als erstes Argumentself hinzufügen

  4. Ändern Sie die Zusicherungen, um die Methodeself.assertEqual()für die KlasseTestCasezu verwenden

  5. Ändern Sie den Befehlszeilen-Einstiegspunkt inunittest.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äuferunittestausgefü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ßtunittestunittest2. 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äufernose2erstellt 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 integrierteassert-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ürTestSumfü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:

  1. Was möchten Sie testen?

  2. Schreiben Sie einen Unit-Test oder einen Integrationstest?

Dann sollte die Struktur eines Tests diesem Workflow lose folgen:

  1. Erstellen Sie Ihre Eingaben

  2. Führen Sie den zu testenden Code aus und erfassen Sie die Ausgabe

  3. 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:

  1. Importiertsum() aus dem von Ihnen erstelltenmy_sum-Paket

  2. Definiert eine neue Testfallklasse namensTestSum, die vonunittest.TestCase erbt

  3. Definiert eine Testmethode,.test_list_int(), um eine Liste von Ganzzahlen zu testen. Die Methode.test_list_int() wird:

    • Deklarieren Sie eine Variabledata mit einer Liste von Zahlen(1, 2, 3)

    • Ordnen Sie das Ergebnis vonmy_sum.sum(data) einer Variablen vonresult zu

    • Stellen Sie sicher, dass der Wert vonresult gleich6 ist, indem Sie die Methode.assertEqual() für die Klasseunittest.TestCase verwenden

  4. Definiert einen Befehlszeilen-Einstiegspunkt, an dem der Testläufer.main() vonunittestausgefü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 vonsum()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

.assertEqual(a, b)

a == b

.assertTrue(x)

bool(x) is True

.assertFalse(x)

bool(x) is False

.assertIs(a, b)

a is b

.assertIsNone(x)

x is None

.assertIn(a, b)

a in b

.assertIsInstance(a, b)

isinstance(a, b)

.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 vonunittestauszufü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*.pyfolgen, 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.pyeine Importanweisung hinzu, um den TypFractionaus dem Modulfractionsin 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:

  1. Die erste Zeile zeigt die Ausführungsergebnisse aller Tests, von denen einer fehlgeschlagen (F) und einer bestanden (.) ist.

  2. Der EintragFAILzeigt 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:

  1. Wählen Sie im Fenster des Projektwerkzeugs das Verzeichnistestsaus.

  2. Wählen Sie im Kontextmenü den Befehl run fürunittest. Wählen Sie beispielsweiseRun ‘Unittests in my Tests…’.

Dadurch werdenunittest in einem Testfenster ausgeführt und Sie erhalten die Ergebnisse in PyCharm:

PyCharm Testing

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:

Visual Studio Code Step 1

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:

Visual Studio Code Step 2

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 Djangostartapphat 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 DjangoTestCaserichtet 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 discoverausfü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:

  1. Erstellen Sie Ihre Eingaben

  2. Führen Sie den Code aus und erfassen Sie die Ausgabe

  3. 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!

Testing Side Effects

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 vonunittestbeide 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:

  1. Test gegen Python 2.7 und 3.7 (Sie können diese Versionen durch eine beliebige ersetzen.)

  2. Installieren Sie alle Pakete, die Sie inrequirements.txt auflisten (Sie sollten diesen Abschnitt entfernen, wenn Sie keine Abhängigkeiten haben.)

  3. Führen Siepython -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 wieE305oben 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 Konfigurationsabschnittflake8intox.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:

Pytest benchmark screenshot

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!