Hallo allerseits!
Wir setzen den
Artikel ĂŒber das Kennenlernen von Python-Tests fort, den wir im Rahmen unseres
Python-Entwicklerkurses fĂŒr Sie vorbereitet haben.
Testen auf Django und Flask Web FrameworkWenn Sie Tests fĂŒr Webanwendungen mit einem der gĂ€ngigen Frameworks schreiben, z. B. Django oder Flask, sollten Sie die wichtigen Unterschiede beim Schreiben und AusfĂŒhren solcher Tests berĂŒcksichtigen.
Wie sie sich von anderen Anwendungen unterscheidenDenken Sie an den Code, den Sie in einer Webanwendung testen möchten. Alle Routen, Ansichten und Modelle erfordern viel Import und Wissen ĂŒber das verwendete Framework.
Dies Ă€hnelt dem Testen eines Autos, das im ersten Teil des Tutorials beschrieben wurde: Bevor Sie einfache Tests durchfĂŒhren, z. B. das ĂberprĂŒfen von Scheinwerfern, mĂŒssen Sie den Computer im Auto einschalten.
Django und Flask vereinfachen diese Aufgabe und bieten ein unittest-basiertes Testframework. Sie können weiterhin Tests auf die ĂŒbliche Weise schreiben, diese jedoch etwas anders ausfĂŒhren.
Verwendung des Django Test Executor
Die Django-Startanwendungsvorlage erstellt die Datei tests.py in Ihrem Anwendungsverzeichnis. Wenn es noch nicht vorhanden ist, erstellen Sie es mit den folgenden Inhalten:
from django.test import TestCase class MyTestCase(TestCase):
Der Hauptunterschied zu frĂŒheren Beispielen besteht darin, dass Sie von
django.test.TestCase
erben
django.test.TestCase
, nicht von
django.test.TestCase
. Die API dieser Klassen ist dieselbe, aber die Django TestCase-Klasse richtet alles zum Testen ein.
Verwenden
manage.py
zum AusfĂŒhren einer Testsuite
manage.py
test anstelle von unittest in der Befehlszeile:
$ python manage.py test
Wenn Sie mehrere Testdateien benötigen, ersetzen Sie tests.py durch den Testordner, fĂŒgen Sie eine leere Datei mit dem Namen
__init__.py
und erstellen Sie
test_*.py
Dateien. Django wird sie erkennen und ausfĂŒhren.
Weitere Informationen finden Sie auf
der Django-Dokumentationsseite .
Verwendung von Unittest und Flask
Um mit Flask arbeiten zu können, muss eine Anwendung importiert und in den Testmodus versetzt werden. Sie können einen Testclient erstellen und damit Anforderungen an beliebige Routen in Ihrer Anwendung senden.
Der Testclient wird in der setUp-Methode Ihres Testfalls instanziiert. Im folgenden Beispiel ist my_app der Name der Anwendung. Machen Sie sich keine Sorgen, wenn Sie nicht wissen, was setUp tut. Wir werden uns den Abschnitt "Erweiterte Testskripte" genauer ansehen.
Der Code in der Testdatei sieht folgendermaĂen aus:
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('/')
Sie können dann TestfÀlle mit dem
python -m unittest discover.
Weitere Informationen finden Sie auf der Flask-Dokumentationsseite.
Erweiterte TestskripteBeachten Sie die drei Hauptschritte eines Tests, bevor Sie mit dem Erstellen von Tests fĂŒr Ihre Anwendung beginnen:
- Erstellung von Eingabeparametern;
- CodeausfĂŒhrung, Empfang von Ausgabedaten;
- Vergleich der Ausgabedaten mit dem erwarteten Ergebnis;
Dies kann komplizierter sein als das Erstellen eines statischen Werts fĂŒr die Quelldaten wie eine Zeichenfolge oder eine Zahl. Manchmal erfordert Ihre Anwendung eine Instanz einer Klasse oder eines Kontexts. Was ist in diesem Fall zu tun?
Die Daten, die Sie als Quelle erstellen, werden als Fixture bezeichnet. Das Erstellen und Wiederverwenden von Fixtures ist eine gÀngige Praxis.
Das mehrfache AusfĂŒhren desselben Tests mit unterschiedlichen Werten in Erwartung des gleichen Ergebnisses wird als Parametrisierung bezeichnet.
Umgang mit erwarteten FehlernAls wir zuvor eine Liste von Skripten zum Testen von
sum()
, stellte sich die Frage: Was passiert, wenn wir einen schlechten Wert angeben, z. B. eine einzelne Ganzzahl oder eine Zeichenfolge?
In diesem Fall wird erwartet, dass
sum()
einen Fehler auslöst. Wenn ein Fehler auftritt, schlÀgt der Test fehl.
Es gibt eine spezielle Möglichkeit, mit erwarteten Fehlern umzugehen. Sie können
.assertRaises()
als Kontextmanager verwenden und dann Testschritte im
with
Block ausfĂŒhren:
import unittest from my_sum import sum class TestSum(unittest.TestCase): def test_list_int(self): """ , """ data = [1, 2, 3] result = sum(data) self.assertEqual(result, 6) def test_list_fraction(self): """ , """ 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 wird nur bestanden, wenn
sum(data)
einen TypeError auslöst. Sie können TypeError durch einen anderen Ausnahmetyp ersetzen.
Isolation des AnwendungsverhaltensIm letzten Teil des Tutorials haben wir ĂŒber Nebenwirkungen gesprochen. Sie erschweren das Testen von Einheiten, da jeder Testlauf zu einem anderen oder einem schlechteren Ergebnis fĂŒhren kann - ein Test kann den Status der gesamten Anwendung beeinflussen und dazu fĂŒhren, dass ein anderer Test fehlschlĂ€gt!
Es gibt einige einfache Techniken zum Testen von Teilen einer Anwendung mit vielen Nebenwirkungen:
- Code-Refactoring gemÀà dem Prinzip der Einzelverantwortung;
- Verspotten aller Methoden und Funktionsaufrufe, um Nebenwirkungen zu beseitigen;
- Verwenden von Integrationstests anstelle von Komponententests fĂŒr dieses Fragment der Anwendung.
- Wenn Sie mit dem Rauchen nicht vertraut sind, sehen Sie sich einige groĂartige Beispiele fĂŒr Python-CLI-Tests an .
Integrationstests schreibenBisher haben wir Unit-Tests mehr Aufmerksamkeit geschenkt. Unit-Tests sind eine groĂartige Möglichkeit, vorhersehbaren und stabilen Code zu erstellen. Aber am Ende sollte Ihre Anwendung beim Start funktionieren!
Integrationstests sind erforderlich, um die Zusammenarbeit mehrerer Anwendungskomponenten zu ĂŒberprĂŒfen. FĂŒr solche Tests muss möglicherweise die Rolle des KĂ€ufers oder Benutzers ausgeĂŒbt werden:
- Rufen Sie die HTTP-REST-API auf.
- Python-API-Aufruf;
- Web-Service-Aufruf;
- FĂŒhren Sie die Befehlszeile aus.
Alle diese Arten von Integrationstests können auf die gleiche Weise wie Komponententests gemÀà der Vorlage Eingabeparameter, AusfĂŒhrung, Genehmigung geschrieben werden. Der wichtigste Unterschied besteht darin, dass Integrationstests gleichzeitig mehr Komponenten testen, was bedeutet, dass sie zu mehr Nebenwirkungen fĂŒhren als Komponententests. DarĂŒber hinaus erfordern Integrationstests mehr GerĂ€te, z. B. eine Datenbank, einen Netzwerk-Socket oder eine Konfigurationsdatei.
Daher wird empfohlen, Unit-Tests und Integrationstests zu trennen. Das Erstellen von Fixtures fĂŒr Integrations-Fixtures, z. B. eine Testdatenbank oder TestfĂ€lle selbst, dauert viel lĂ€nger als das DurchfĂŒhren von Unit-Tests. Daher sollten Sie Integrationstests durchfĂŒhren, bevor Sie in die Produktion gehen, anstatt sie jedes Mal auszufĂŒhren, wenn Sie einen Commit ausfĂŒhren.
Die einfachste 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
Sie können eine bestimmte Gruppe von Tests auf verschiedene Arten ausfĂŒhren. Ein Flag zur Angabe des Quellverzeichnisses -s kann hinzugefĂŒgt werden, um die Erkennung mit einem Pfad zu vereinen, der Tests enthĂ€lt:
$ python -m unittest discover -s tests/integration
unittest zeigt alle Ergebnisse im Test- / Integrationsverzeichnis an.
Testen datenorientierter AnwendungenViele Integrationstests erfordern Backend-Daten, z. B. eine Datenbank mit bestimmten Werten. Angenommen, Sie benötigen einen Test, um den korrekten Betrieb der Anwendung mit mehr als 100 Kunden in der Datenbank zu ĂŒberprĂŒfen oder um die Richtigkeit der Anzeige der Bestellseite zu ĂŒberprĂŒfen, selbst wenn alle Namen der Waren auf Japanisch sind.
Diese Arten von Integrationstests hÀngen von verschiedenen Testvorrichtungen ab, um ihre Wiederholbarkeit und Vorhersagbarkeit sicherzustellen.
Testdaten sollten im Ordner "Fixtures" im Verzeichnis "Integrationstests" gespeichert werden, um ihre "Testbarkeit" hervorzuheben. Dann können Sie in den Tests die Daten laden und den Test ausfĂŒhren.
Hier ist ein Beispiel fĂŒr eine Datenstruktur, die aus JSON-Dateien besteht:
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
Im Testfall können Sie die Methode .setUp () verwenden, um Testdaten auf bekannte Weise aus der Fixture-Datei zu laden und mehrere Tests mit diesen Daten auszufĂŒhren. Denken Sie daran, dass Sie mehrere TestfĂ€lle in einer Python-Datei speichern können. Unittest findet sie und fĂŒhrt sie aus. Sie können einen Testfall fĂŒr jeden Satz von Testdaten haben:
import unittest class TestBasic(unittest.TestCase): def setUp(self):
Wenn Ihre Anwendung von Daten von einem Remotestandort abhĂ€ngt, z. B. von einer Remote-API, stellen Sie sicher, dass die Tests wiederholbar sind. Die Entwicklung kann sich aufgrund von Tests verzögern, die beim Deaktivieren der API und bei Kommunikationsproblemen fehlgeschlagen sind. In solchen FĂ€llen ist es besser, Remote-GerĂ€te lokal zu speichern, um sie zurĂŒckzurufen und an die Anwendung zu senden.
Die
requests
verfĂŒgt ĂŒber ein kostenloses Antwortpaket, mit dem Sie Antwortvorrichtungen erstellen und in Testordnern speichern können. Weitere
Informationen finden Sie
auf der GitHub-Seite .
Im nÀchsten Teil geht es um das Testen in verschiedenen Umgebungen und das Testen der Automatisierung.
DAS ENDE
Kommentare / Fragen sind immer willkommen. Hier oder an einem
Tag der offenen TĂŒr nach
Stas .