Entdecken Sie, was Pytest ist und wofür es verwendet wird, und vergleichen Sie es mit anderen Softwaretestmethoden.
Tests sind ein großes Thema in der Softwareentwicklung. Bevor ein Softwareprodukt in die Hände eines Endbenutzers gelangt, hat es wahrscheinlich mehrere Tests durchlaufen, beispielsweise Integrationstests, Systemtests und Abnahmetests. Die Idee hinter solch gründlichen Tests besteht darin, sicherzustellen, dass das Verhalten der Anwendung aus Sicht des Endbenutzers wie erwartet funktioniert. Dieser Testansatz wird als verhaltensgesteuerte Entwicklung (Behavior-Driven Development, BDD) bezeichnet.
In jüngster Zeit ist das Interesse an testgetriebener Entwicklung (TDD) unter Entwicklern deutlich gestiegen. In diesem Artikel näher darauf einzugehen, ist möglicherweise eine komplexe Aufgabe, aber die Grundidee besteht darin, dass der traditionelle Prozess von Entwicklung und Test umgekehrt wird: Sie schreiben zuerst Ihre Unit-Tests und implementieren dann Codeänderungen, bis die Tests erfolgreich sind.
In diesem Artikel konzentrieren wir uns auf Unit-Tests und insbesondere darauf, wie diese mit einem beliebten Python-Test-Framework namens Pytest durchgeführt werden.
Was sind Unit-Tests?
Unit-Tests sind eine Form automatisierter Tests. Das bedeutet einfach, dass der Testplan von einem Skript und nicht manuell von einem Menschen ausgeführt wird. Sie dienen als erste Ebene des Softwaretests und werden normalerweise in Form von Funktionen geschrieben, die das Verhalten verschiedener Funktionen innerhalb eines Softwareprogramms validieren.
Die Idee hinter diesen Tests ist, dass Entwickler die kleinste logisch sinnvolle Codeeinheit isolieren und testen können, ob sie sich wie erwartet verhält. Mit anderen Worten: Unit-Tests bestätigen, dass die einzelnen Komponenten eines Softwareprogramms so funktionieren, wie es die Entwickler beabsichtigt haben.
Idealerweise sollten diese Tests relativ klein sein – je kleiner, desto besser. Ein Grund für kleinere Tests ist, dass der Test effizienter ist, da der Testcode durch das Testen kleinerer Einheiten viel schneller ausgeführt werden kann. Ein weiterer Grund für das Testen kleinerer Komponenten ist, dass Sie dadurch einen besseren Einblick in das Verhalten des granularen Codes beim Zusammenführen erhalten.
Warum brauchen wir Unit-Tests?
Die allgemeine Begründung dafür, warum Unit-Tests unbedingt erforderlich sind, ist, dass Entwickler sicherstellen müssen, dass der von ihnen geschriebene Code den Qualitätsstandards entspricht, bevor sie ihn in eine Produktionsumgebung einführen. Allerdings tragen auch mehrere andere Faktoren zur Notwendigkeit von Unit-Tests bei. Lassen Sie uns einige dieser Gründe genauer untersuchen.
Ressourcen schonend
Durch die Durchführung von Unit-Tests können Entwickler Codefehler bereits während der Software-Entwicklungsphase erkennen und verhindern, dass diese später im Entwicklungszyklus auftreten. Dadurch werden Ressourcen geschont, da Entwickler später im Entwicklungsprozess nicht für die Behebung von Fehlern aufkommen müssen. Außerdem müssen sich Endbenutzer seltener mit fehlerhaftem Code herumschlagen.
Zusätzliche Dokumentation
Ein weiterer guter Grund für die Durchführung von Unit-Tests ist, dass sie als zusätzliche Ebene der lebendigen Dokumentation für Ihr Softwareprodukt dienen. Entwickler können einfach auf die Unit-Tests zurückgreifen, um ein ganzheitliches Verständnis des Gesamtsystems zu erhalten, da sie detailliert beschreiben, wie sich die kleineren Komponenten verhalten sollen.
Selbstvertrauensschub
Es ist extrem einfach, beim Schreiben einer Funktionalität subtile Fehler in Ihrem Code zu machen. Die meisten Entwickler würden jedoch zustimmen, dass es viel besser ist, die Schwachstellen in einer Codebasis zu identifizieren, bevor sie in eine Produktionsumgebung eingefügt wird: Unit-Tests bieten Entwicklern diese Möglichkeit.
Man kann durchaus sagen, dass „Code, der mit Unit-Tests abgedeckt ist, als zuverlässiger angesehen werden kann als Code, der nicht getestet wurde“. Zukünftige Codefehler können viel schneller entdeckt werden als Code ohne Testabdeckung, was Zeit und Geld spart. Entwickler profitieren außerdem von zusätzlicher Dokumentation, sodass sie die Codebasis schneller verstehen können. Außerdem haben sie die zusätzliche Sicherheit, dass Fehler in ihrem Code von den Unit-Tests und nicht vom Endbenutzer erkannt werden.
Python-Testframeworks
Python hat im Laufe der Jahre enorm an Popularität gewonnen. Im Zuge des Wachstums von Python hat auch die Anzahl der Testframeworks zugenommen, was zu einer Fülle von Tools geführt hat, mit denen Sie Ihren Python-Code testen können. Auf die Einzelheiten der einzelnen Tools einzugehen, würde den Rahmen dieses Artikels sprengen, wir werden jedoch auf einige der gängigsten verfügbaren Python-Testframeworks eingehen.
Gerätetest
Unittest ist ein integriertes Python-Framework für Unittests. Es wurde von einem Unittest-Framework namens JUnit aus der Programmiersprache Java inspiriert. Da es mit der Python-Sprache vorinstalliert ist, müssen keine zusätzlichen Module installiert werden und die meisten Entwickler verwenden es, um sich mit dem Testen vertraut zu machen .
Pytest
Pytest ist möglicherweise das am weitesten verbreitete Python-Testframework überhaupt. Dies bedeutet, dass es eine große Community gibt, die Sie unterstützt, wenn Sie nicht weiterkommen. Es ist ein Open-Source-Framework, mit dem Entwickler einfache, kompakte Testsuiten schreiben können und das gleichzeitig Unit-Tests, Funktionstests und API-Tests unterstützt.
Doctest
Das Doctest- Framework vereint zwei Kernkomponenten der Softwareentwicklung: Dokumentation und Testen. Diese Funktionalität stellt sicher, dass alle Softwareprogramme gründlich dokumentiert und getestet werden, um sicherzustellen, dass sie wie vorgesehen funktionieren. Doctest wird mit der Standardbibliothek von Python geliefert und ist ziemlich einfach zu erlernen.
Nase2
Nose2 , der Nachfolger von Nose Regiment, ist im Wesentlichen Unittest mit Plugins. Nose2 wird oft als „erweiterter Unittest“ oder „Unittest mit Plugin“ bezeichnet, da es eng mit dem in Python integrierten Unittest-Framework verknüpft ist. Da es praktisch eine Erweiterung des Unittest-Frameworks ist, ist Nose2 für diejenigen, die mit Unittest vertraut sind, unglaublich einfach zu übernehmen.
Bezeugen
Testify , ein Python-Framework für Unit-, Integrations- und Systemtests, ist allgemein als das Framework bekannt, das Unittest und Nose ersetzen sollte. Das Framework ist mit umfangreichen Plugins ausgestattet und hat eine recht sanfte Lernkurve, wenn Sie bereits mit Unittest vertraut sind.
Hypothese
Mit Hypothesis können Entwickler Unit-Tests erstellen, die einfacher zu schreiben und bei der Ausführung leistungsstark sind. Da das Framework zur Unterstützung von Data-Science-Projekten entwickelt wurde, hilft es beim Auffinden von Randfällen, die beim Erstellen Ihrer Tests nicht so offensichtlich sind, indem es Beispiele für Eingaben generiert, die mit den von Ihnen definierten spezifischen Eigenschaften übereinstimmen.
Für unser Tutorial verwenden wir pytest. Im nächsten Abschnitt erfahren Sie, warum Sie sich für Pytest und nicht für die anderen hier aufgeführten Tools entscheiden sollten.
Warum Pytest verwenden?
Neben seiner großen unterstützenden Community hat pytest mehrere Faktoren, die es zu einem der besten Tools für die Durchführung Ihrer automatisierten Testsuite in Python machen. Die Philosophie und Funktionen von Pytest sind darauf ausgerichtet, Softwaretests zu einem viel besseren Entwicklererlebnis zu machen. Die Entwickler von Pytest haben dieses Ziel unter anderem dadurch erreicht, dass sie die für die Ausführung allgemeiner Aufgaben erforderliche Codemenge erheblich reduziert und die Ausführung erweiterter Aufgaben mit umfangreichen Befehlen und Plug-Ins ermöglicht haben.
Weitere Gründe für die Verwendung von Pytest sind die folgenden:
Leicht zu lernen
Pytest ist extrem einfach zu erlernen: Wenn Sie verstehen, wie das Schlüsselwort assert von Python funktioniert, sind Sie bereits auf dem besten Weg, das Framework zu beherrschen. Tests, die pytest verwenden, sind Python-Funktionen mit vorangestelltem „test_“ oder „_test“ an den Funktionsnamen angehängt – Sie können jedoch auch eine Klasse verwenden, um mehrere Tests zu gruppieren. Insgesamt ist die Lernkurve für pytest viel flacher als bei unittest und ähnlichen Programmen, da Sie keine neuen Konstrukte lernen müssen.
Testfilterung
Möglicherweise möchten Sie nicht alle Ihre Tests bei jeder Ausführung ausführen – dies kann der Fall sein, wenn Ihre Testsuite wächst. Manchmal möchten Sie vielleicht einige Tests für eine neue Funktion isolieren, um während der Entwicklung schnelles Feedback zu erhalten, und dann die gesamte Suite ausführen, wenn Sie sicher sind, dass alles wie geplant funktioniert. Pytest bietet drei Möglichkeiten, Tests zu isolieren: 1) namensbasiertes Filtern, das pytest anweist, nur die Tests auszuführen, deren Namen dem bereitgestellten Muster entsprechen, 2) Verzeichnisbereich, eine Standardeinstellung, die pytest anweist, nur Tests auszuführen, die sich im aktuellen Verzeichnis oder darunter befinden, und 3) Testkategorisierung, die es Ihnen ermöglicht, Kategorien für Tests zu definieren, die pytest einschließen oder ausschließen soll.
Parametrierung
Pytest verfügt über einen integrierten Dekorator namens „parametrize“, der die Parametrisierung von Argumenten für eine Testfunktion ermöglicht. Wenn die von Ihnen getesteten Funktionen also Daten verarbeiten oder eine generische Transformation durchführen, müssen Sie nicht mehrere ähnliche Tests schreiben. Wir werden später in diesem Artikel mehr auf die Parametrisierung eingehen .
Wir werden hier aufhören, aber die Liste, warum pytest eine großartige Tool-Option für Ihre automatisierte Testsuite ist, geht noch weiter.
Pytest vs. Unittest
Trotz aller oben genannten Gründe kann die Verwendung von pytest immer noch angezweifelt werden, und zwar aus dem einfachen Grund, dass es sich um ein Framework eines Drittanbieters handelt: „Was bringt es, ein Framework zu installieren, wenn bereits eines integriert ist?“ Das ist ein gutes Argument, aber um uns in dieser Auseinandersetzung abzusichern, möchten wir Ihnen einige Dinge nennen, die Sie berücksichtigen sollten.
Hinweis : Wenn Sie von pytest bereits überzeugt sind, fahren Sie mit dem nächsten Abschnitt fort, in dem wir uns mit der Verwendung des Frameworks befassen.
Weniger Standardtext
Unittest erfordert, dass Entwickler Klassen erstellen, die vom TestCase- Modul abgeleitet sind, und dann die Testfälle als Methoden in der Klasse definieren.
Bei Pytest hingegen müssen Sie lediglich eine Funktion mit vorangestelltem „test_“ definieren und darin die Assert-Bedingungen verwenden.
Beachten Sie den Unterschied in der erforderlichen Codemenge. unittest erfordert eine erhebliche Menge an Boilerplate-Code, der als Mindestanforderung für jeden Test dient, den Sie durchführen möchten. Dies bedeutet, dass Sie höchstwahrscheinlich denselben Code mehrmals schreiben werden. Pytest hingegen verfügt über umfangreiche integrierte Funktionen, die diesen Arbeitsablauf vereinfachen, indem sie die zum Schreiben von Testfällen erforderliche Codemenge reduzieren.
Ausgabe
Die von den einzelnen Frameworks bereitgestellten Ausgaben sind äußerst unterschiedlich. Hier ist ein Beispiel für eine pytest-Ausführung:
Der obige Testfall ist fehlgeschlagen, aber beachten Sie, wie detailliert die Aufschlüsselung des Fehlers ist. Dadurch können Entwickler leichter feststellen, wo in ihrem Code Fehler vorhanden sind, was beim Debuggen äußerst hilfreich ist. Als Bonus gibt es auch einen Gesamtstatusbericht für die Testsuite, der uns die Anzahl der fehlgeschlagenen Tests und deren Dauer angibt.
Sehen wir uns ein Beispiel für einen fehlgeschlagenen Testfall mit unittest an.
Wenn wir das Skript ausführen, erhalten wir die folgende Ausgabe:
Die obige Ausgabe zeigt uns, dass zwei Tests in 0,001 s ausgeführt wurden und einer fehlschlug, aber sonst nicht viel. Letztlich liefert pytest viel informativeres Feedback, was beim Debuggen nützlich ist.
Alles in allem sind sowohl pytest als auch unittest großartige Tools für automatisierte Tests in Python. Einige Python-Entwickler bevorzugen aufgrund seiner Kompaktheit und Effizienz eher pytest als seine Gegenstücke. Es ist außerdem extrem einfach zu übernehmen und es gibt mehrere Funktionen, mit denen Sie eine effektive Testsuite erstellen können.
Nun zum Hauptteil dieses Artikels. Wir haben besprochen, was Unit-Tests sind und warum pytest ein großartiges Tool für automatisierte Tests in Python ist. Sehen wir uns nun an, wie man das Tool verwendet.
Pytest-Tutorial
Sehen wir uns an, wie dieses Python-Testframework funktioniert, über das wir gesprochen haben.
Der erste Schritt ist die Installation des Pakets, was mit einem einfachen Pip-Befehl durchgeführt werden kann.
Hinweis: Die Entwickler von pytest empfehlen, venv für die Entwicklung und pip für die Installation Ihrer Anwendung, Abhängigkeiten und pytest selbst zu verwenden .
Überprüfen Sie als Nächstes mit dem folgenden Befehl, ob das Framework installiert wurde:
Alles ist installiert. Jetzt können Sie mit der Durchführung einiger Tests beginnen.
Erstellen eines einfachen Tests
Mit Pytest ist das Erstellen eines Tests ganz einfach. Um diese Funktionalität zu demonstrieren, haben wir ein Skript namens calcualte_age.py erstellt . Dieses Skript hat nur eine Funktion, get_age, die für die Berechnung des Alters eines Benutzers anhand seines Geburtsdatums verantwortlich ist.
Pytest führt alle Python-Dateien aus, denen der Name test_ vorangestellt oder _test an den Namen des Skripts angehängt ist. Genauer gesagt befolgt pytest die folgenden Konventionen für die Testerkennung [Quelle: Dokumentation ]:
- Wenn keine Argumente angegeben sind, beginnt die pytest-Sammlung in Testpfaden, sofern diese konfiguriert sind: Testpfade sind eine Liste von Verzeichnissen, die pytest durchsucht, wenn keine bestimmten Verzeichnisse, Dateien oder Test-IDs angegeben sind.
- Pytest würde dann in Verzeichnisse rekursiv vorgehen, sofern Sie ihm nicht durch Setzen von norecursedirs dies untersagt haben . Es sucht nach Dateien, die mit test_*.py beginnen oder mit *_test.py enden.
- In diesen Dateien sammelt pytest Testelemente in der folgenden Reihenfolge:
- Vorangestellte Testfunktionen oder -methoden außerhalb der Klasse
- Mit dem Präfix versehene Testfunktionen oder -methoden innerhalb von Test-Präfix-Testklassen, die keine __init__-Methode haben.
Wir haben keine Argumente angegeben, aber wir haben ein weiteres Skript im selben Verzeichnis namens test_calculate_age.py erstellt : Wenn die Verzeichnisse rekursiv durchsucht werden, wird der Test daher gefunden. In diesem Skript haben wir einen einzelnen Test, test_get_age, um zu überprüfen, ob unsere Funktion ordnungsgemäß funktioniert.
Hinweis : Sie können Ihre Tests auch in einem zusätzlichen Verzeichnis außerhalb Ihrer Anwendung ablegen. Dies ist beispielsweise dann sinnvoll, wenn Sie über mehrere Funktionstests verfügen oder den Testcode und den Anwendungscode aus anderen Gründen getrennt halten möchten.
Um den Test auszuführen, führen Sie den folgenden Befehl in der Eingabeaufforderung aus:
Und das ist alles.
Aber was, wenn wir einige Daten bereitstellen müssen, damit unsere Tests erfolgreich sind? Dann sind PyTest-Fixtures genau das Richtige für Sie contemporary art.
Pytest-Vorrichtungen
Die unschätzbare Fixture-Funktion von Pytest ermöglicht es Entwicklern, Daten in die Tests einzuspeisen. Dabei handelt es sich im Wesentlichen um Funktionen, die vor jeder Testfunktion ausgeführt werden, um den Status unserer Tests zu verwalten. Angenommen, wir haben mehrere Tests, die alle dieselben Daten verwenden, dann können wir ein Fixture verwenden, um die wiederholten Daten mithilfe einer einzigen Funktion abzurufen.
Im obigen Code haben wir mit dem Dekorator pytest.fixture ein Fixture erstellt. Beachten Sie, dass der Bereich auf „Sitzung“ eingestellt ist, um pytest mitzuteilen, dass das Fixture am Ende der Testsession zerstört werden soll.
Hinweis : Unsere Vorrichtungen sind in conftest.py gespeichert .
Der Code verwendet eine Funktion, die wir aus einem anderen Modul importiert haben, um zwei CSV-Dateien in den Speicher zu laden und zu einem einzigen Datensatz zusammenzuführen. Danach wird der Datensatz in Trainings- und Testsätze aufgeteilt und von der Funktion zurückgegeben.
Um dieses Fixture in unseren Tests zu verwenden, müssen wir es als Parameter an unsere Testfunktion übergeben. In diesem Beispiel verwenden wir unser Fixture in unserem Testskript test_pipeline.py wie folgt:
Machen Sie sich nicht zu viele Gedanken darüber, was der Code macht. Das Wichtigste, was wir hervorheben möchten, ist, wie wir die Notwendigkeit, redundanten Code zu schreiben, erheblich reduziert haben, weil wir ein Fixture erstellt haben, das wir als Parameter übergeben, um Daten in unsere Tests zu ziehen.
Es gibt jedoch Situationen, in denen Fixtures übertrieben sein können. Wenn beispielsweise die in Ihre Tests eingebundenen Daten in jedem Testfall erneut verarbeitet werden müssen, ist dies praktisch gleichbedeutend damit, Ihren Code mit mehreren einfachen Objekten zu überladen. Abgesehen davon werden Fixtures in Ihrer Testsuite wahrscheinlich eine entscheidende Rolle spielen, aber zu erkennen, wann sie verwendet oder vermieden werden sollten, erfordert Übung und viel Überlegung.
Pytest parametrisieren
Fixtures sind großartig, wenn Sie mehrere Tests mit denselben Eingaben haben. Was ist, wenn Sie eine einzelne Funktion mit leichten Variationen der Eingaben testen möchten? Eine Lösung besteht darin, mehrere verschiedene Tests mit verschiedenen Fällen zu schreiben.
Diese Lösung funktioniert zwar, ist aber nicht die effizienteste: Zunächst einmal gibt es jede Menge Standardcode. Eine bessere Lösung ist die Verwendung des Dekorators pytest.mark.parametrize(), um die Parametrisierung von Argumenten für eine Testfunktion zu ermöglichen. Dadurch können wir eine einzelne Testdefinition definieren, und dann testet pytest die verschiedenen Parameter, die wir für uns angeben.
So würden wir den obigen Code umschreiben, wenn wir die Parametrisierung verwenden würden:
Der Dekorator @parametrize definiert vier verschiedene Testeingaben und erwartete Werte für die Ausführung der Funktion test_eval. Dies bedeutet, dass die Funktion viermal ausgeführt wird und dabei nacheinander jeden einzelnen verwendet.
In diesem Artikel haben wir die folgenden Themen behandelt:
- was Unit-Tests sind
- warum wir Unit-Tests brauchen
- verschiedene Test-Frameworks in Python
- Warum pytest so nützlich ist
- wie man pytest und zwei seiner Hauptfunktionen (Fixtures und Parametrisierung) verwendet
Sie wissen jetzt genug, um mit dem Schreiben Ihres eigenen Tests mit dem pytest-Framework zu beginnen. Wir empfehlen Ihnen dringend, dies zu tun, damit alles, was Sie in diesem Artikel gelernt haben, hängen bleibt. Sie sollten sich auch unseren Kurs zum Unit-Testen für Data Science in Python ansehen , um eine ausführlichere Erklärung mit Beispielen zu erhalten.