Der Newbie-Leitfaden zur testgetriebenen Entwicklung

Das Testen Ihres Codes ist ärgerlich, aber die Auswirkungen, wenn Sie dies nicht tun, kann um Größenordnungen ärgerlicher sein! In diesem Artikel verwenden wir testgetriebene Entwicklung, um unseren Code effektiver zu schreiben und zu testen.


Was ist testgetriebene Entwicklung??

Seit dem Beginn des Computerzeitalters haben Programmierer und Fehler um die Vorherrschaft gekämpft. Es ist ein unvermeidliches Ereignis. Sogar die größten Programmierer fallen diesen Anomalien zum Opfer. Kein Code ist sicher. Deshalb testen wir. Programmierer, zumindest vernünftige, testen ihren Code, indem sie ihn auf Entwicklungscomputern ausführen, um sicherzustellen, dass er das tut, was er soll.


Vernünftiger Programmierer, der seine Programme testet.
Bild mit freundlicher Genehmigung von http://www.youthedesigner.com
Verrückter Programmierer, der seine Programme nicht testet.
Bild mit freundlicher Genehmigung von http://www.internetannoyanceday.com

Testgetriebene Entwicklung ist eine Programmiertechnik, bei der Sie gleichzeitig aktuellen Code und automatisierten Testcode schreiben müssen. Dies stellt sicher, dass Sie Ihren Code testen - und ermöglicht Ihnen, Ihren Code schnell und einfach erneut zu testen, da er automatisiert ist.

Wie funktioniert es?

Testgetriebene Entwicklung, oder TDD, wie wir es von nun an nennen, dreht sich um einen kurzen iterativen Entwicklungszyklus, der etwa so abläuft:

  1. Bevor Sie Code schreiben, müssen Sie zunächst einen automatisierten Test für Ihren Code schreiben. Beim Schreiben der automatisierten Tests müssen Sie alle möglichen Eingaben, Fehler und Ausgaben berücksichtigen. Auf diese Weise wird Ihr Verstand nicht von bereits geschriebenem Code getrübt.
  2. Wenn Sie den automatisierten Test zum ersten Mal ausführen, sollte der Test fehlschlagen. Dies bedeutet, dass der Code noch nicht bereit ist.
  3. Danach können Sie mit der Programmierung beginnen. Da es bereits einen automatisierten Test gibt, bedeutet dies, dass der Test noch nicht fertiggestellt ist, solange der Code fehlschlägt. Der Code kann festgelegt werden, bis alle Zusicherungen bestanden sind.
  4. Sobald der Code den Test bestanden hat, können Sie ihn über Refactoring mit der Bereinigung beginnen. Solange der Code den Test noch besteht, funktioniert er weiterhin. Sie müssen sich nicht mehr um Änderungen kümmern, die neue Fehler verursachen.
  5. Starten Sie das Ganze erneut mit einer anderen Methode oder einem anderen Programm.

Der testgetriebene Entwicklungszyklus
Bild mit freundlicher Genehmigung von http://en.wikipedia.org/wiki/Test-driven_development

Großartig, aber wie ist das besser als regelmäßige Tests?

Haben Sie schon einmal das Testen eines Programms übersprungen, weil:

  • Sie fanden es eine Zeitverschwendung zum Testen, da es nur eine geringfügige Codeänderung war?
  • Sie fühlten sich faul, alles wieder zu testen?
  • Sie hatten nicht genug Zeit, um zu testen, weil der Projektmanager wollte, dass das Produkt so schnell wie möglich in die Produktion übergeht?
  • Sie sagten sich, Sie würden es "morgen" tun?
  • Sie mussten wählen, ob Sie manuell testen möchten oder ob Sie die neueste Episode Ihrer Lieblingsfernsehsendung (Big Bang Theory) sehen.?

Meistens passiert nichts, und Sie können Ihren Code ohne Probleme erfolgreich in die Produktion bringen. Aber manchmal, nachdem Sie zur Produktion übergegangen sind, geht alles schief. Sie stecken fest und reparieren hundert Löcher in einem sinkenden Schiff, wobei jede Minute mehr erscheint. Sie machen nicht Ich möchte mich in dieser Situation wiederfinden.


Verschrauben Sie es, bringen Sie es einfach zur Produktion!
Bild mit freundlicher Genehmigung von http://phenomenaonbreak.wordpress.com

TDD sollte unsere Ausreden beseitigen. Wenn ein Programm mit TDD entwickelt wurde, können wir Änderungen schnell und effizient durchführen und testen. Wir müssen nur die automatisierten Tests durchführen und voila! Wenn alle automatisierten Tests bestanden sind, können wir loslegen. Wenn nicht, bedeutet dies nur, dass wir mit den Änderungen etwas gebrochen haben. Durch das Wissen, welche genauen Teile des Tests fehlgeschlagen sind, können wir auch leicht feststellen, an welchem ​​Teil der Änderungen der Fehler aufgetreten ist. Dies erleichtert die Behebung der Fehler.


Ich bin verkauft. Wie machen wir das?

Es gibt eine Vielzahl von PHP-Frameworks für automatisierte Tests, die wir verwenden können. Eines der am häufigsten verwendeten Testframeworks ist PHPUnit.

PHPUnit ist ein großartiges Testframework, das sich leicht in eigene Projekte oder andere Projekte integrieren lässt, die auf gängigen PHP-Frameworks aufbauen.

Für unsere Zwecke benötigen wir jedoch nicht die Vielzahl von Funktionen, die PHPUnit bietet. Stattdessen entscheiden wir uns für die Erstellung unserer Tests mit einem viel einfacheren Test-Framework namens SimpleTest.

Nehmen wir in den nächsten Schritten an, dass wir eine Gästebuchanwendung entwickeln, in der jeder Benutzer Gästebucheinträge hinzufügen und anzeigen kann. Nehmen wir an, dass das Markup abgeschlossen wurde und wir einfach eine Klasse erstellen, die das enthält Anwendungslogik des Gästebuchs, wo die Anwendung die Datenbank einfügt und liest. Der Leseteil dieser Klasse ist das, was wir entwickeln und testen werden.


Schritt 1. SimpleTest einrichten

Dies ist wohl der einfachste Schritt von allen. Sogar dieser Typ könnte es schaffen:


Ich kann dies tun? Kann ich verwenden, mein, ähm? Gehirn!
Bild mit freundlicher Genehmigung von http://longstreet.typepad.com/

Laden Sie SimpleTest hier herunter und entpacken Sie es in einen Ordner Ihrer Wahl - vorzugsweise den Ordner, in dem Sie Ihren Code entwickeln, oder Ihren PHP include_path für den einfachen Zugriff.

Für dieses Tutorial habe ich den Ordner so eingerichtet:

Index.php führt guestbook.php aus, ruft die View-Methode auf und zeigt die Einträge an. Innerhalb des Klassenordners legen wir die guestbook.php-Klasse ab, und im Testordner platzieren wir die einfachste Bibliothek.


Schritt 2. Planen Sie Ihren Angriff


Bild mit freundlicher Genehmigung von http://connections.smsd.org/veterans

Der zweite und eigentlich wichtigste Schritt besteht darin, mit der Erstellung Ihrer Tests zu beginnen. Zu diesem Zweck müssen Sie wirklich planen und darüber nachdenken, was Ihre Funktion bewirkt, welche möglichen Eingänge sie erhalten und welche Ausgänge sie senden werden. Dieser Schritt ähnelt einem Schachspiel. Sie müssen alles über Ihren Gegner (das Programm) wissen, einschließlich aller Schwächen (mögliche Fehler) und Stärken (was passiert, wenn er erfolgreich ausgeführt wird)..

Lassen Sie uns für unsere Gästebuch-Anwendung die Schaltpläne festlegen:

Aussicht

  • Diese Funktion hat keine Eingaben, da sie lediglich alle Einträge aus der Datenbank abruft und die Daten zum Ausdrucken zurücksendet.
  • Es wird eine Reihe von Gästebuchdatensätzen mit dem Namen des Posters und seiner Nachricht zurückgegeben. Wenn keine Datensätze vorhanden sind, sollte dennoch ein leeres Array zurückgegeben werden.
  • Wenn Datensätze vorhanden sind, enthält das Array einen oder mehrere Werte.
  • Zur gleichen Zeit hat das Array eine bestimmte Struktur, etwa:
 Array ([0] => Array (['name'] = "Bob" ['message'] = "Hi, ich bin Bob.") [1] => Array (['name'] = "Tom") ['message'] = "Hallo, ich bin Tom."))

Schritt 3. Schreiben Sie einen Test!


Bild mit freundlicher Genehmigung von http://cflhomeless.wordpress.com

Nun können wir unseren ersten Test schreiben. Beginnen wir mit dem Erstellen einer Datei namens guestbook_test.php im Testordner.

  

Dann konvertieren wir das, was wir aus Schritt zwei bestimmt haben,.

 add ("Bob", "Hi, ich bin Bob."); $ guestbook-> add ("Tom", "Hallo, ich bin Tom."); $ entries = $ guestbook-> viewAll (); $ count_is_greater_than_zero = (count ($ entries)> 0); $ this-> assertTrue ($ count_is_greater_than_zero); $ this-> assertIsA ($ entries, 'array'); foreach ($ entries as $ entry) $ this-> assertIsA ($ entry, 'array'); $ this-> assertTrue (isset ($ entry ['name'])); $ this-> assertTrue (isset ($ entry ['message']));  Funktion testViewGuestbookWithNoEntries () $ guestbook = new Guestbook (); $ guestbook-> deleteAll (); // Lösche zuerst alle Einträge, damit wir wissen, dass es sich um eine leere Tabelle handelt. $ Entries = $ guestbook-> viewAll (); $ this-> assertEqual ($ entries, array ()); 

Assertions stellen sicher, dass eine bestimmte Sache das ist, was sie sein soll - im Grunde stellt sie sicher, dass das, was zurückgegeben wird, das ist, was Sie erwarten, dass es zurückkehrt. Wenn zum Beispiel eine Funktion true zurückgeben soll, wenn sie erfolgreich ist, sollten wir dies in unserem Test tun behaupten dass der Rückgabewert gleich true ist.

Wie Sie hier sehen, testen wir die Anzeige des Gästebuches mit und ohne Einträge. Wir prüfen, ob diese beiden Szenarien unsere Kriterien aus Schritt zwei erfüllen. Sie haben wahrscheinlich auch bemerkt, dass jede unserer Testfunktionen mit dem Wort "Test" beginnt. Wir haben dies getan, da SimpleTest diese Klasse ausführt und nach allen Funktionen sucht, die mit dem Wort 'test' beginnen, und es ausführen.

In unserer Testklasse haben wir auch einige Assertionsmethoden verwendet, wie assertTrue, assertIsA und assertEquals. Die Funktion assertTrue prüft, ob ein Wert wahr ist oder nicht. AssertIsA prüft, ob eine Variable einen bestimmten Typ oder eine bestimmte Klasse hat. Als letztes überprüft assertEquals, ob eine Variable einem bestimmten Wert entspricht.

Es gibt andere Bestätigungsmethoden, die von SimpleTest bereitgestellt werden, und zwar:

assertTrue ($ x) Scheitern, wenn $ x falsch ist
assertFalse ($ x) Fehler, wenn $ x wahr ist
assertNull ($ x) Fail, wenn $ x gesetzt ist
assertNotNull ($ x) Fehler, wenn $ x nicht gesetzt ist
assertIsA ($ x, $ t) Fehler, wenn $ x nicht die Klasse oder der Typ $ t ist
assertNotA ($ x, $ t) Fehler, wenn $ x der Klasse oder dem Typ $ t entspricht
assertEqual ($ x, $ y) Fehler, wenn $ x == $ y falsch ist
assertNotEqual ($ x, $ y) Fehler, wenn $ x == $ y wahr ist
assertWithinMargin ($ x, $ y, $ m) Scheitern, wenn abs ($ x - $ y) < $m is false
assertOutsideMargin ($ x, $ y, $ m) Scheitern, wenn abs ($ x - $ y) < $m is true
assertIdentical ($ x, $ y) Schlagen Sie fehl, wenn $ x == $ y falsch ist oder ein Typkonflikt besteht
assertNotIdentical ($ x, $ y) Fehler, wenn $ x == $ y wahr ist und die Typen übereinstimmen
assertReference ($ x, $ y) Fehler, es sei denn, $ x und $ y sind dieselbe Variable
assertClone ($ x, $ y) Schlagen Sie fehl, wenn $ x und $ y identische Kopien sind
assertPattern ($ p, $ x) Scheitern, es sei denn, der Regex $ p stimmt mit $ x überein
assertNoPattern ($ p, $ x) Fehler, wenn der Regex $ p mit $ x übereinstimmt
expectError ($ x) Verschluckt jeden anstehenden Übereinstimmungsfehler
behaupten ($ e) Fehler bei fehlgeschlagenem Erwartungsobjekt $ e

Liste der Assertionsmethoden mit freundlicher Genehmigung von http://www.simpletest.org/de/unit_test_documentation.html


Schritt 4. Nicht gewinnen


Bild mit freundlicher Genehmigung von http://verydemotivational.com

Wenn Sie mit dem Schreiben des Codes fertig sind, sollten Sie den Test ausführen. Wenn Sie den Test zum ersten Mal ausführen, Sollte fehlschlagen. Wenn dies nicht der Fall ist, bedeutet dies, dass Ihr Test nichts wirklich testet.

Um den Test auszuführen, führen Sie einfach aus guestbook_test.php in Ihrem Browser Sie sollten dies zuerst sehen:

Dies geschah, weil wir unsere Gästebuchklasse noch nicht erstellt haben. Erstellen Sie dazu die Datei guestbook.php in Ihrem Klassenordner. Die Klasse sollte die Methoden enthalten, die wir verwenden möchten, aber zunächst noch nichts. Denken Sie daran, wir schreiben die Tests zuerst Vor einen beliebigen Code schreiben.

  

Wenn Sie den Test erneut ausführen, sollte er ungefähr so ​​aussehen:

Wie wir hier sehen können, gewinnt unser Test jetzt, wenn er versagt. Dies bedeutet, dass unser Test jetzt bereit ist, "beantwortet" zu werden.


Bild mit freundlicher Genehmigung von http://www.gamercastnetwork.com/forums

Schritt 5. Beantworten Sie Ihren Test, indem Sie Code schreiben


Irgendwann haben wir uns alle beim Programmieren so gefühlt.
Bild mit freundlicher Genehmigung von http://fermentation.typepad.com/fermentation

Jetzt, da wir einen funktionierenden automatisierten Test haben, können wir mit dem Schreiben von Code beginnen. Öffne deine guestbook.php Klasse und beginnen Sie, die Antwort auf Ihren Test zu erstellen.

  'Kirk', 'message' => 'Hi, ich bin Kirk.' ), array ('name' => 'Ted', 'message' => 'Hi, ich bin Ted.')); public function viewAll () // Hier sollten wir alle Datensätze aus der Datenbank abrufen. // Dies wird simuliert, indem das Array $ _entries zurückgegeben wird. Return self :: $ _ entries;  public function add ($ name, $ message) // Hier simulieren wir das Einfügen in die Datenbank, indem wir einen neuen Datensatz in das $ _entries-Array einfügen. // Dies ist der richtige Weg: self :: $ _ entries [] = Array ('name' => $ name, 'message' => $ message); self :: $ _ entries [] = array ('notname' => $ name, 'notmessage' => $ message); // oops, hier gibt es einen Fehler, irgendwo zurückgeben true;  public function deleteAll () // Wir setzen das $ _entries-Array so, dass es self :: $ _ entries = array (); wahr zurückgeben; 

Diese guestbook.php-Klasse enthält absichtlich einige Fehler, sodass wir sehen können, wie es aussieht, wenn unser Test fehlschlägt.

Sobald wir unseren Test durchgeführt haben, sollten wir so etwas sehen:

Die Testausgabe zeigt uns, bei welchem ​​Test und bei welcher Aussage unser Code fehlgeschlagen ist. Daraus lässt sich leicht feststellen, dass die Zeile 16 und 17 die Behauptung war, die den Fehler ausgelöst hat.

 assertTrue (isset ($ entry ['name'])); $ this-> assertTrue (isset ($ entry ['message'])) ;? 

Dies zeigt uns eindeutig, dass das zurückgegebene Eintragsfeld nicht den richtigen Feldschlüssel hatte. Auf dieser Grundlage wissen wir leicht, welcher Teil unseres Codes fehlerhaft ist.

  $ name, 'message' => $ message); //Fest! wahr zurückgeben;  

Nun, wenn wir unseren Test erneut durchführen, sollte es uns zeigen:


Schritt 6. Refactor und Verfeinern Sie Ihren Code


Bilder mit freundlicher Genehmigung von http://www.osborneink.com und http://phuketnews.phuketindex.com

Da der Code, den wir hier testen, ziemlich einfach ist, dauerten unser Testen und die Fehlerbehebung nicht sehr lange. Wenn es sich jedoch um eine komplexere Anwendung handelt, müssen Sie Ihren Code mehrfach ändern, sauberer machen, damit er einfacher zu warten ist und viele andere Dinge. Das Problem dabei ist jedoch, dass Änderungen in der Regel zusätzliche Fehler verursachen. Hier kommt unser automatisierter Test ins Spiel - sobald wir Änderungen vorgenommen haben, können wir den Test einfach noch einmal durchführen. Wenn es immer noch durchgeht, bedeutet das, dass wir nichts kaputt gemacht haben. Wenn es fehlschlägt, wissen wir, dass wir einen Fehler gemacht haben. Es informiert uns auch, wo das Problem liegt und wie wir es hoffentlich beheben können.


Schritt 7. Spülen und wiederholen


Mit freundlicher Genehmigung von http://www.philstockworld.com

Wenn Ihr Programm neue Funktionen erfordert, müssen Sie möglicherweise neue Tests schreiben. Das ist einfach! Spülen Sie die Prozeduren ab Schritt zwei und wiederholen Sie sie (da Ihre SimpleTest-Dateien bereits eingerichtet sein sollten), und starten Sie den Zyklus erneut.


Fazit

Es gibt viel tiefgreifendere, testgetriebene Entwicklungsartikel und noch mehr Funktionalität für SimpleTest, als in diesem Artikel dargestellt wurde. Dazu gehören Mock-Objekte und Stubs, die das Erstellen von Tests erleichtern. Wenn Sie mehr erfahren möchten, sollten Sie auf der testgetriebenen Entwicklungsseite von Wikipedia den richtigen Weg finden. Wenn Sie SimpleTest als Testframework verwenden möchten, durchsuchen Sie die Online-Dokumentation und stellen Sie sicher, dass Sie auch die anderen Funktionen überprüfen.

Das Testen ist ein wesentlicher Bestandteil des Entwicklungszyklus, es ist jedoch zu oft das erste, was bei unmittelbar bevorstehenden Fristen gekürzt werden muss. Wenn Sie diesen Artikel gelesen haben, werden Sie hoffentlich schätzen, wie hilfreich es ist, in testgetriebene Entwicklung zu investieren.

Was halten Sie von testgetriebener Entwicklung? Interessieren Sie sich für die Implementierung oder denken Sie, dass es Zeitverschwendung ist? Lass es mich in den Kommentaren wissen!