Einführung:
Test-Driven Development (TDD) ist eine wichtige Praxis im Software Engineering, die darauf abzielt, die Softwarequalität zu verbessern und Fehler zu reduzieren. Dieser Artikel behandelt TDD und dessen Umsetzung im Kontext der Flutter-Entwicklung. Wir werden die Stufen des TDD, dessen Vorteile und wie es zur Erstellung zuverlässiger und robuster Softwarelösungen beiträgt, untersuchen.
STLS:
Testen ist der Prozess, möglichst viele Defekte zu entdecken, und einer der Prozesse, die zur Durchführung von Tests angewendet werden können, ist der STLC, das steht für Software Testing Life Cycle (Lebenszyklus des Softwaretests). Dieser Prozess besteht aus sechs Stufen wie folgt:
1. Anforderungsanalyse: In dieser Phase arbeitet das Testteam daran, die Projektanforderungen zu verstehen. Diese Phase ist entscheidend, um Missverständnisse der Anforderungen zu vermeiden, die zu ungültigen Tests führen könnten.
2. Testplanung: In dieser Phase wird ein Testplan erstellt, und mehrere Faktoren werden bestimmt, einschließlich:
a. Definieren des Umfangs des Tests, Identifizieren der Funktionen und Merkmale, die getestet werden sollen, und derjenigen, die nicht getestet werden sollen.
b. Identifizieren der Attribute, die das Projekt erreichen sollte und die getestet werden müssen, wie Funktionalität, Sicherheit, Benutzerfreundlichkeit usw.
c. Dokumentieren aller während des Testprozesses erzeugten Ausgaben, einschließlich:
i. Test-Szenarien: Diese umfassen die möglichen Szenarien, die ausgeführt werden müssen, um die Software zu testen. Sie beschreiben, wie die Tests ausgeführt werden sollen und was getestet werden soll.
ii. Testfälle: Diese umfassen die detaillierten Schritte, die erforderlich sind, um jedes Testszenario auszuführen. Sie beschreiben, wie der Test durchgeführt werden soll und wie die Ergebnisse zu bewerten sind.
iii. Testdaten: Diese umfassen spezifische Daten, die in den Tests verwendet werden müssen, einschließlich Eingaben, erwarteter Ausgaben und anderer relevanter Daten.
3. Entwicklung von Testfällen: In dieser Phase werden die im Testplanungsstadium definierten Testfälle geschrieben.
4. Aufbau der Testumgebung: Die Testumgebung bezieht sich auf die Software und Hardware, durch die die Anwendung getestet wird. In dieser Phase werden die für den Test erforderlichen Elemente überprüft und eingerichtet.
5. Testausführung: In dieser Phase werden die Tests durchgeführt, und die Ergebnisse werden überwacht. Bei auftretenden Fehlern erfolgt die Kommunikation mit dem Entwicklungsteam, um die Probleme zu lösen.
6. Abschluss des Testzyklus: Dies ist die letzte Stufe im Testprozess, in der die Ergebnisse analysiert und bewertet werden.
TDD:
Es ist die Abkürzung für Test Driven Development (TDD), was bedeutet, Tests vor dem Schreiben des eigentlichen (Produktions-)Codes zu erstellen.
Durch diesen Ansatz ersetzen wir die Stufen der Entwicklung eines Softwareprodukts. Hier schreiben wir zuerst den Testcode und dann den eigentlichen (Produktions-)Code.
TDD-Stufen:
TDD wird im Allgemeinen durch die folgenden Schritte umgesetzt:
- Schreiben Sie einen kleinen, spezifischen Test für eine bestimmte Funktion.
- Führen Sie den Test aus und überprüfen Sie sein Scheitern (rot).
- Schreiben Sie den erforderlichen Code, um den Test zu bestehen (Übergang von rot zu grün).
- Führen Sie den Test aus und überprüfen Sie seinen Erfolg (grün).
- Refaktorisieren Sie den Code (Duplikate entfernen, Wiederverwendbarkeit verbessern, etc.).
- Wiederholen Sie den obigen Prozess für andere Fälle und Funktionen.
TDD-Beispiel:
Nehmen wir eine Funktion in Betracht, deren Aufgabe es ist, eine mathematische Operation, genauer gesagt die Addition, durchzuführen. Im traditionellen Ansatz würden wir zuerst die Additionsfunktion schreiben, dann die Testfälle definieren und schließlich die Testfälle auf die Funktion anwenden und die Ergebnisse vergleichen.
Bei TDD würden wir damit beginnen, die Testfälle für die Additionsfunktion zu schreiben. Zum Beispiel, wenn uns zwei Zahlen, x1 und x2, gegeben werden, erwarten wir, dass das Ergebnis x3 ist. Dann schreiben wir den Code spezifisch für diesen Testfall und führen den Test aus. Zu diesem Zeitpunkt würde das Ergebnis aufgrund der noch nicht geschriebenen eigentlichen Code-Logik einen Fehler aufweisen (rot).
Als Nächstes gehen wir zur Erstellung des eigentlichen Codes für die Additionsfunktion über. Sobald wir mit dem Schreiben fertig sind, führen wir den zuvor geschriebenen Testcode aus. Hier gibt es zwei Möglichkeiten: Entweder besteht der Test (grün), und dann gehen wir zu einem anderen Testfall über, oder er schlägt fehl. Im Falle eines Fehlers modifizieren wir den eigentlichen Code und testen ihn erneut, bis er besteht (grün wird).
TDD-Regeln:
- Es ist nicht erlaubt, irgendwelchen Produktionscode zu schreiben, es sei denn, es dient dazu, einen fehlschlagenden Unit-Test zu bestehen.
- Es ist nicht erlaubt, mehr von einem Unit-Test zu schreiben, als notwendig ist, um zu scheitern; und Kompilierungsfehler gelten als Fehler.
- Es ist nicht erlaubt, mehr Produktionscode zu schreiben, als notwendig ist, um den einen fehlschlagenden Unit-Test zu bestehen.
Vorteile von TDD:
- TDD hilft, Fehler in den frühen Entwicklungsstadien zu vermeiden, da wir von Anfang an Tests für jede Funktion erstellen. Diese Tests werden wiederholt ausgeführt, um den Erfolg der Funktion zu überprüfen und das Fehlen von Fehlern sicherzustellen.
- TDD trägt dazu bei, die Softwarequalität zu verbessern, indem präzise und spezifische Tests für jede Funktion von Anfang an erstellt werden. Als Ergebnis wird der Code so geschrieben, dass er mit diesen Tests in Einklang steht und ihre Erreichung sicherstellt. Wenn der notwendige Code geschrieben wird, um diese Tests zu bestehen, und er kontinuierlich im Laufe des Entwicklungsprozesses verbessert und bereinigt wird, ist der resultierende Code sauber und optimiert, ohne überflüssige oder unnötige Teile. Es verbessert auch die Flexibilität und Wartbarkeit des Codes in der Zukunft und trägt somit zur Verbesserung der Softwarequalität bei und reduziert Probleme und Fehler auf lange Sicht.
- TDD hilft bei der Entwicklung von Code-Logik. Indem Tests für jede Funktion von Anfang an geschrieben und wiederholt ausgeführt werden, können Entwickler diese Tests verwenden, um die Code-Logik beim Hinzufügen weiterer Funktionalitäten zu leiten. Mit einfacheren Funktionen zu beginnen, ermöglicht es Entwicklern, das Problem in kleinere, handhabbarere Teile aufzuteilen. Dies erleichtert das bessere und effizientere Problemlösen.
TDD mit Flutter:
Wir können die Testfälle schreiben, die wir erreichen möchten, bevor wir den eigentlichen Code (Produktionscode) mithilfe der von Flutter bereitgestellten Tests schreiben.
Wir wissen, dass es drei Arten von Tests in Flutter gibt: Unit Test, Widget Test und Integration Test. In diesem Artikel konzentrieren wir uns auf das Unit Testing.
Unit test in flutter:
Der Unit Test ist eine Art von Softwaretest, der auf die kleinste Einheit des Codes angewendet wird (z. B. eine Funktion – Methode usw.).
Der Unit Test durchläuft drei Phasen:
Anordnen: In dieser Phase wird alles vorbereitet, was für die Durchführung des Tests notwendig ist. Dies umfasst das Einrichten der erforderlichen Bedingungen für die erfolgreiche Ausführung des Tests, das Initialisieren von Werten, Variablen oder Eingaben, die für den Test benötigt werden.
Handeln: Die zu testende Einheit wird auf der Grundlage der in der vorherigen Phase vorbereiteten Bedingungen ausgeführt. Das Ergebnis der Ausführung der Einheit wird in einer festgelegten Variable gespeichert, um sie später in der dritten Phase zu verwenden.
Überprüfen: In dieser Phase wird das Ergebnis der Ausführung der Einheit überprüft und mit dem erwarteten Ergebnis verglichen, das in der ersten Phase vorbereitet wurde. Diese Phase beinhaltet die Verwendung von assert-Funktionen, um die Richtigkeit des Ergebnisses zu überprüfen und das Auftreten erwarteter Ereignisse zu überprüfen, wie das Aufrufen einer bestimmten Funktion und das Sicherstellen, dass die getestete Einheit wie erwartet funktioniert.
Beispiel:
Es gibt eine Klasse, die eine Funktion namens „increment“ enthält. Das Ziel besteht darin, dass diese Funktion den Wert von „value“ jedes Mal um eins erhöht, wenn sie aufgerufen wird.
Wir folgen dem Ansatz der Testgetriebenen Entwicklung (TDD) im Entwicklungsprozess. Wir erstellen zwei Dateien: die erste heißt „counter.dart“ und die zweite ist die Testdatei „counter_test.dart“.
Die Klasse „Counter“ wird die Funktion enthalten, die wir TDD anwenden möchten. Es ist wichtig zu beachten, dass wir keine Logik innerhalb dieser Funktion schreiben sollten, bevor wir den entsprechenden Unit Test schreiben.
Wie in der folgenden Abbildung gezeigt:
Wir schreiben den Inkrement-Unit-Test.
Das Ergebnis wird fehlschlagen (ROT) sein, weil wir die Logik für die Inkrementierung noch nicht geschrieben haben.
Jetzt gehen wir zum eigentlichen (Produktions-)Code und schreiben die Inkrement-Logik.
Nachdem wir die Logik für die „increment“-Funktion geschrieben haben, müssen wir den Unit Test erneut ausführen, um sicherzustellen, dass die Funktion wie erwartet funktioniert.
Wir stellen fest, dass der Test bestanden hat.
Schlussfolgerung:
Nach mehreren Jahren der Arbeit in der Softwareentwicklung ist deutlich geworden, dass die testgetriebene Entwicklung (TDD) unerlässlich ist, um hochwertige Software zu erstellen und Fehler im Code zu reduzieren. TDD trägt dazu bei, die Effektivität des Entwicklungsprozesses zu steigern und die Qualität des endgültigen Produkts zu verbessern.
Daher wird die Verwendung von TDD als gute Investition zur Steigerung der Qualität und Produktivität des Entwicklungsprozesses angesehen.