PHP Blogger

Startseite Schreib mir ne Mail! RSS Abo Webnews

Debugging sucks, Testing rocks!

Nachdem ich diese Woche gelernt habe wie Google das Testen von Applikationen verbessert, dachte ich, dass ist ein guter Anlass, um sich zum Thema zu äußern. Google hat damit angefangen kleine Snippets über das Testen auf den Toiletten aufzuhängen. Da kann man nicht nur notwendige Körperliche Funktionen erledigen, sondern auch gleich noch was über das Programmieren lernen. So nötig haben wir das noch nicht, aber das Thema kommt immer wieder hoch.

Bisher habe ich Frameworks wie PHP-Unit immer noch nicht richtig verstanden. Zum einen, weil mir nie jemand richtig erklären konnte, wie ich sinnvolle Testcases schreibe, die beachten, dass Funktionen unter Umständen Datenbankeinträge machen und Daten verändern und vor allem, wenn ich eine Funktion ändere, einen Test ausführe und dieser scheitert, woher weiß ich, dass der Test durch meine Änderung nicht falsch geworden ist. Dann weiß ich zwar, dass irgendwas nicht stimmt, aber vielleicht ist ja mein Testfall nicht mehr korrekt.

Also habe ich immer manuell getestet. Das könnte sich jetzt ändern! Denn ich habe Selenium entdeckt.

Selenium ist in der Basisversion ein Firefox-Plugin, mit dem man Makros aufzeichnen kann. Soweit so gut. Aber man kann eben auch “Testfälle” erstellen. Nach dem Motto:

  • Rufe die Seite ‘xyz’ auf.
  • Überprüfe, ob die folgenden Inputfelder vorhanden sind
  • Klicke auf den Submit-Button
  • Überprüfe, ob der Text “Formular erfolgreich verschickt” auf der nächsten Seite auftaucht

Das ist ziemlich cool geht meinem Geschmack nach genau von der Seite ans Testen, die ich immer wollte. Ich will nämlich nicht auf Methoden-Ebene testen, sondern auf Funktions-Ebene.

Man kann auch mehrere Testcases zusammenfassen in einer Suite. Damit kann man sich eine Repertoire aufbauen und wenn man jetzt etwas an seiner Applikation ändert, dann lässt man die Testsuite ablaufen und sieht, auf welchen Seiten es Probleme gibt, weil bsp. ein Formular nicht mehr abgeschickt wird. Oder man kann testen, ob der Test “Fatal Error” nicht auf der Seite erscheint.
Getreu nach dem Google Motto: Debugging sucks, Testing rocks! habe ich bisher nur auf die richtige Testanwendung gewartet und nun womöglich gefunden.

Ähnliche Artikel:

  1. PHPUnit - Wo soll man anfangen

Iljitsch meint dazu:

21. November 2008 um 22:13

Hi - netter Hinweis aber ganz ehrlich versuch lieber xUnit zu verstehen das ist weit wertvoller und so groß ist der Aufwand nicht. Schau dir vllt. mal Mock-Objects und Fixtures an dann haste au den Einstieg um complexere Sachen zu testen. Btw gibt nen gutes Buch von Kent Beck zum Thema.
Grüße

Stefan meint dazu:

21. November 2008 um 23:20

Selenium ist sehr gut für Frontend Testing, und Unit-Testing ist sehr gut für - ähm - das Testen von Units. Wenn man seine Applikation wirklich gut testen möchte, dann sollte man mindestens beide Frameworks einsetzen, ggf. erweitert um automatisierte Integrations- und Performance Tests.

oh - und um das Debugging kommt man trotzdem garantiert nicht rum ;)

phil meint dazu:

22. November 2008 um 05:35

Super gerne… Habt ihr denn ein paar online Resourcen, wo ich Beispiele finde, die man auch sinnvoll für Webapplikationen anwenden kann? Weil alles was ich immer zu dem xUnit Thema finde ist:
a) Das Beispiel mit dem Konto, das niemals unter Null rutschen darf
b) API Beschreibungen…

Aber damit kann ich leider wenig anfangen… Da ist mir Selenium alle mal lieber. Oder gibt es auch schon javascriptUnit?

Ulf meint dazu:

22. November 2008 um 11:56

Hi Phil,

du bringst ein paar Sachen durcheinander. Unit-Testing, Frontend-Testing und Debugging ergänzen sich gegenseitig, heben sich aber niemals auf! Mit besseren Testing wirst du sicherlich seltener debuggen, aber niemals wirst du ganz darauf verzichten können.

Und du schreibst in deinem Blog-Eintrag dass sich u.U. die Testfälle bei einer Änderung im Quelltext ändern. Genau das musst du eben auch anpassen bevor du den Test noch einmal durchläufst. Genau das nennt man dann auch Test-Driven-Development. Änderungen in der Applikationslogik werden bei jedem Programm immer passieren, aber gerade in den Tests wird/muss dies dokumentiert werden.

Und ich glaube du hast grundsätzliche Probleme das Prinzip von Unit-Testing zu verstehen. Hast du mal angefangen dich bei Wikipedia hineinzulesen (http://de.wikipedia.org/wiki/Modultest)? Was verstehst du denn konkret nicht? Wo hast du deine Probleme?

Viele Grüße
Ulf

phil meint dazu:

22. November 2008 um 19:28

Hi Ulf,

danke für die Antwort. Vielleicht hast du recht, ich habe nämlich oft eine Outside-In (Selenium) Sicht auf die Dinge und bei Unit-Testing habe ich das Gefühl, dass ich das Konzept vom Inside-Out (vom Kleinen zum Großen und jeden möglichen Fall abdecken) nicht auf meine Art zu programmieren übertragen kann.

Also konkret, wie ich Unit-Testing verstehe:
Ich möchte eine Klasse programmieren, die bsp. einen Blog-Eintrag repräsentiert. Dann hat diese Klasse vielleicht Variablen wie Title, Datum, Inhalt. Und Methoden __construct und getter und setter. So, jetzt kann ich meinen ersten Test schreiben, der für mich wenig Sinn macht, aber ich teste meinen __construct, getter und setter.
Als nächstes habe ich eine Klasse BlogCollection. Diese hat Funktionen wie getBlogs, getBlogById, insertBlog, updateBlog, deleteBlog. Diese Funktionen benutzen nun aber die DB API, um sich zu die Daten abzurufen und da fangen meine Verständnisprobleme von Unit-Testing richtig an, weil ich nirgends ein Beispiel finde, wie ich eine 3-Tier Applikation teste.

Jetzt spricht hier jemand von Mock-Objects… Aber meines Verständnisses von Mock-Objects müsste ich die BlogCollection als Mock Object implementieren und wie soll ich dann meine eigentliche BlogCollection testen?

Und nein, die Diskussion ist nicht über ob ich mein Programmiermodell ans Testen anpassen müsste, sondern darüber, ob sich Unit Testen auf mein Programmiermodell anwenden lässt.

Dirk meint dazu:

23. November 2008 um 01:49

–”..wenn ich eine Funktion ändere, einen Test ausführe und dieser scheitert, woher weiß ich, dass der Test durch meine Änderung nicht falsch geworden ist.”–

Das ist falsch herum gedacht. Test driven development heißt immer: “Zuerst den Test schreiben, dann die Funktion”. Deine Tests können also nie out of date sein, weil sie die ersten sind, die angepasst werden (und damit eine Anforderung an deine Funktion definieren).

–”Und nein, die Diskussion ist nicht über ob ich mein Programmiermodell ans Testen anpassen müsste, sondern darüber, ob sich Unit Testen auf mein Programmiermodell anwenden lässt.”–

Ich fürchte doch. Denn wenn du Unit Testing ernst nimmst, musst du deine Programmierung so umstellen, dass eben immer zuerst die Test kommen und dann deine Funktionen, um einen spezifischen Test zu erfüllen.

Beispiel für einen Test für insertBlog (sehr pseudo-code):
- truncate table (blogentries)
- ret = insertBlog(’Test’, ‘Das ist ein Test’, ‘01.01.1970′);
- assertTrue(ret)
- row = select * from blogentries
- assertEqual(count(blogentries), 1)
- assertEqual(row['title'], ‘Test’)
- assertEqual(row['content'], ”Das ist ein Test”)
- assertEqual(row['date'], ‘01.01.1970′)

phil meint dazu:

23. November 2008 um 04:05

Also, ich habe eine Applikation mit 50 Klassen. Jede Klasse hat 10-15 Methode mit ca. 3-4 Parametern. Selbst wenn das Boolsche Parameter sind, dann habe ich 16 * 15 * 50 = 12000 Testfälle, nur um alleine die Möglichen Parameterkombinationen zu erfassen. Wie soll ich da Test-Driven Development ernst nehmen? Es tut mir leid, aber das kann man alleine nicht leisten…

Aber wo sind denn jetzt die tollen Resourcen, die mal verständliche Beispiele bringen, die über mehr als eine in sich abgeschlossene Klasse hinweggehen?

Und um mal noch ein weiteres Fass aufzumachen. Was ist mit Zustand? Wie kann ich Methoden testen, die von äußerem Zustand abhängen? Muss ich dann jeden möglichen Zustand auch in meinen Test reincodieren?!

Dirk meint dazu:

23. November 2008 um 05:10

Also - dein Hauptproblem ist, dass du eine bestehende Infrastruktur hast und diese nun mit Unit-Tests testen willst. Das funktioniert nur, wie du richtig bemerkt hast, mit enormem Aufwand. Deshalb sollte sinnvollerweise von vorne herein “Test driven” entwickelt werden. Das Problem hast du aber immer, wenn du mitten in der Entwicklung das Paradigma wechseln willst. Beispiel: ein bestehendes prozedurales Programm auf OOP umstellen.

Grundsätzlich gilt: deine Tests sollten so einfach wie möglich sein. Es ist sinnvoll für jede Produktivklasse eine Testklasse anzulegen und entsprechend für jede Methode genau eine Testfunktion. In dieser Testfunktion können mehrere Tests ablaufen, wie im Beispiel gezeigt. Das ist dann jeweils nur eine Zeile, um einen Wert zu prüfen. In den meisten Fällen wirst du einzelne Parameter zu einem Test zusammenfassen können. Außerdem werden deine Tests selten so komplex, wie die Methoden sein. In so fern sind deine 12.000 Tests doch recht hoch gegriffen. Im Durchschnitt habe ich etwa 4 Tests pro Funktion, also in deinem Beispiel 50*15*4 = 3000. Das ist viel, aber ein Programm mit 50 Klassen a 10 Methoden hat entweder sehr kleine Methoden oder ist für sich schon ein ziemlich großes Biest. Mein Test-Code ist etwa 1/3 meines Produktiv-Codes, nur zum Vergleich.

Tests sollen sich eigentlich nicht über eine Klasse hinwegbewegen. Wenn eine Klasse B auf einer anderen Klasse A aufbaut (sie erweitert oder ihre Methoden nutzt), dann muss Klasse A vor Klasse B getestet werden. Selbiges gilt, wenn man Methoden in einem Test nutzen will, die in diesem Moment noch nicht getestet sind.

Das mit dem Zustand ist aber in der Tat so eine Sache. Besonders beim Session Management oder der Nutzerverwaltung wird dir das begegnen. Meine Antwort darauf (die eventuell falsch ist): ich mache die grundlegenden Methoden so klein wie möglich (schwarz/weiß) und teste diese mit einem richtigen und einem falschen Zustand. Aus diesen ja/nein Mathoden-Bausteinen baue ich mir komplexere Abläufe.

Leider leider gibt es zu Unit Testing und PHP kaum Dokumentation. Ich habe das meiste aus der SimpleTest Doku und den Beispielen. Ich verwende Unit Testing sicherlich nicht 100% richtig. Aber letztendlich ist es wie jedes andere Programmier-Pragma auch: man nimmt das, was einen hilft und lässt anderes weg, was einem eben nicht passt.

phil meint dazu:

23. November 2008 um 05:24

:)

Ich glaube darauf können wir aufbauen.

Sven meint dazu:

24. November 2008 um 11:26

Danke für den Tipp, kannte ich bisher nicht und werde es mal ausprobieren :)

Thorsten meint dazu:

24. November 2008 um 11:37

Richtig geil…danke!

Ulf meint dazu:

25. November 2008 um 12:40

Hallo Phil,

es ist zwar schon ein paar Tage her, aber ich hoffe du bist noch immer wissbegierig… ;)

@Test-Driven-Development
Du hast es schon richtig bemerkt, du wirst niemals alle deine einzelnen Units auf alle Zustände und Möglichkeiten testen können. Wenn dir es aber gelingt, deine Units auf die wichtigsten und bedeutesten Zustände zu testen, dann hast du schon etwas erreicht. Denn in diesem Fall hast du für diese Anwendungsfälle Test-Sicherheit. Was wichtig und bedeutend ist, definierst im übrigen du selbst. Da du den Test vor der eigentlichen Implementierung der Unit schreiben sollst, machst du dir darüber zwangsläufig Gedanken, was bedeutsam für deine Anwendung ist. Man wird mit Testen niemals 100%-ige Fehlerfreiheit erreichen, aber doch bedeutend mehr als ohne (vor allem bei größeren Software-Projekten).

@Testen von ineinander abhängigen Klassen
Da solltest man sich grundsätzlich hinterfragen ob das Software-Design an dieser Stelle richtig ist. Große Abhängigkeiten zwischen Klassen widersprechen ja dem Prinzip des “loose coupling”. Wenn diese Abhängigkeiten aber bestehen müssen, dann gibt es über setup und reset-Methoden sowie Mock-Objeckts durchaus die Möglichkeit dies zu testen. Es erhöht nur den Testaufwand - der Preis für schlechtes Software-Design.

Ich hoffe dir damit etwas geholfen zu haben. Im übrigen darfst du nicht frustieren. Ich habe mich am Anfang auch gefragt wieso es Test-Driven-Development gibt und bin daran teilweise fast verzweifelt. Viel Geduld, viel Probierei und viele Diskussionen mit meinem Chef haben mich von der Notwendigkeit überzeugt. Und jetzt bin ich selber Verfechter davon, man sieht es ja! ;)

Viele Grüße aus Berlin
Ulf

hype.yeebase.com meint dazu:

25. November 2008 um 17:11

Debugging sucks, Testing rocks!…

Plädoyer mit kurzer Einführung in das Testframework “Selenium”. Selenium ist in der Basisversion ein Firefox-Plugin, mit dem man Makros aufzeichnen kann….

phil meint dazu:

25. November 2008 um 18:48

Ich bin immer wissbegierig. Glaub mir Ulf, ich denke jede Woche mindestens zweimal darüber nach, wie ich meinen Missstand, dass ich eben keine Tests für die Applikation habe, beheben kann. Aber wie Dirk schon richtig bemerkt hat, ist es noch mal so schwierig, wenn man bereits eine Infrastruktur hat nachträglich die Tests einzufügen.

Die App habe ich angefangen, bevor ich im Studium all die tollen Sachen über TTD gehört habe (was immer noch nicht genug war, um es für mich so verständlich zu machen, dass ich weiß was ich tun müsste), aber das Dilemma versuche ich ja gerade zu beheben und wahrscheinlich hast du recht, ich muss einfach mehr ausprobieren, anstatt das Problem schon mal im Kopf lösen zu wollen, vielleicht lösen sich meine Fragen dann ganz von alleine.

Zum Loose-Coupling ist noch zu sagen, dass man damit ja nicht meint, dass ein Objekt niemals eine Funktion in einem anderen Objekt aufrufen darf, sondern nur, dass diese Aufrufe über schön definierte Interfaces stattfinden, so dass man nicht merkt, wenn man in dem aufgerufenen Objekt etwas ändert, bzw. das aufrufende Objekt nicht ebenfalls ändern muss. Und das ist bei mir soweit der Fall.

Schlechtes Software-Design ist da etwas schwammig. Für den Anwendungsfall, für den die Applikation ist (CMS, 1 Developer, Kudenprobleme lösen) ist das Software-Design mehr als optimal, für den Anwendungsfall TTD kann ich dir die Frage eben nicht beantworten, aber ich würde sagen, dass ich da vielleicht Defizite aufweise ;)

P.S: Herzlichen Glückwunsch, wenn dein Chef TTD ausgibt, dann wüßte ich zu gerne wo du arbeitest. Weil da wo ich früher gearbeitet habe, da war es nur sehr schwer von unten zu vermitteln, wofür Software Design gut sein soll (da wusste ich ja noch nichts von TTD, hätte aber wenigstens gerne eine sinnvolle Planung gemacht) und wer das bezahlen soll, also musste ich das so lala nebenher machen! :)

Flug meint dazu:

26. November 2008 um 16:57

Hey leute. Mich würde jetzt interessieren für welchen weg ihr euch entscheiden würdet !Wird nun selenium oder xUnit bevorzugt genutzt ? ich stehe quasi hier vor dem gleichen Problem wie Phil- verstehe also irgendwie immer noch nicht wirklich programme wie ” Php-Unit”.
Wenn jemand von euch den “königsweg” kennt scheut euch bitte nicht eine klare Aussage zu treffen auch wenn es warscheinlich nur eure eigene Meinung ist.
MFG

phil meint dazu:

26. November 2008 um 22:42

Hi Flug,

wie du der Diskussion entnehmen kannst, ist das schwierig. Der Königsweg ist natürlich, dass man beides hat. xUnit + Frontend.

Meine Meinung ist, dass du das testen solltest, was für dich wichtiger ist, in meinem Fall ist es das, was der Kunde sieht und benutzt und was “funktionieren” muss. Wenn ich einen Anruf bekomme, dass etwas nicht funktioniert, dann will ich die Tests laufen lassen können, um zu sagen: Das Problem ist nicht die Seite, sondern xyz.

Aber die Jungs vom Test-Driven-Development haben schon recht, dass wenn du oft deine Funktionen neu schreibst, oder Code änderst, oder viele Leute an dem Projekt gleichzeitig arbeiten, dann würde ich auf jeden Fall xUnit vorziehen.

Hoffe das hilft?

Gruß
Philipp

Flug meint dazu:

27. November 2008 um 13:44

Hey philipp

Danke für die schnelle Antwort!
Ich schreibe meine Funktionen oft neu und ändere die codes “minütlich”:-P
Also werde ich mich wohl mal etwas eingehender mit xUnit auseinandersetzen!

Das es keinen Königsweg gibt war mir auch schon vorher bewußt , ich wollte nur mal schauen was ihr für erfahrungen gemacht habt, wollte hören womit ihr am besten klar kommt!

Nun ja wie gesagt - Du hast mir auf jeden fall geholfen , ich hoffe das bei weiteren fragen genau so schnell geantwortet wird. Daumen hoch!

Lg, Flug

Ulf meint dazu:

28. November 2008 um 19:31

Hi Phil,

sorry mal wieder für die späte Antwort, aber ich musste angestrent die Woche arbeiten… ;)

@TDD
Ich finde toll dass du es im Studium gelernt bzw. zumindest angeschnitten hast. Bei uns kam das leider gar nicht vor. :( Ich habe diese Woche aber selbst damit zu kämpfen gehabt, eine sinnvolle Testumgebung für ein Mini-Projekt aufzusetzen. Da stellt sich natürlich immer die Frage von Aufwand/Kosten Nutzen. Ich habe die Testumgebung sehr klein gehalten, aber für dieses Projekt war es noch ok.

Ich bin aber der Meinung sobald mehr als zwei Leute an einem Projekt arbeiten, geht quasi kein Weg daran vorbei. Denn jemand der etwas selber programmiert hat soviel Insider-Wissen, dass kann man gar nicht vermitteln. Bei xUnit-Tests fällt aber eine Änderung im Rückgabetyp bzw. in der Signatur immer auf. Es wird eben klar gekapselt und diese Kapselung wird unabhängig davon noch einmal getestet.

@Loose-Coupling
Loose-Coupling definiert nicht nur dass Sub-Systeme nur über Schnittschnellen sprechen dürfen sondern es definiert auch, dass Sub-Systeme so wenig wie möglich miteinander kommunizieren sollen. Ein guter Einstieg ins Thema ist hierbei natürlich wieder Wiki (http://en.wikipedia.org/wiki/Loose_coupling). Aber auch die GoF haben immer wieder darauf hingewiesen. Und imo ist dieser Punkt Punkt um ein Vielfaches schwieriger umzusetzen als ein xUnit-Test-Setup. Ich kann beides nicht. :( ;)

@Sonstiges
Den Chef und die Firma kann ich dir gerne sagen, aber das bitte lieber per E-Mail. Ich glaube du solltest als Moderator Zugriff auf meine E-Mail haben. Wenn es dich also interessiert. Hetzer- bzw. Herzereien haben nichts in der Öffentlichkeit verloren. ;)

Viele Grüße und ein schönes Wochenende
Ulf

phil meint dazu:

4. Dezember 2008 um 19:44

Hi Ulf,

ich habe noch mal ein bisschen weiter in die Materie reingelesen. Das Thema lässt mich nicht los und ich glaube mittlerweile, dass ich

1. Zu viel mit Unit Test machen wollte.
2. Da Aspekte von anderen “Testarten” (Funktionale Tests die zum Beispiel mit Selenium gemacht werden können) reingemischt habe.

Ich werde einfach mal anfangen ein bisschen auszuprobieren, ob und wie ich vielleicht noch ein paar sinnvolle Tests für meine Applikation bauen kann, damit ich besser für die Zukunft gerüstet bin.

Das Thema ist echt spannend.

Selenium zweckentfremdet | Der PHP Hacker meint dazu:

20. Dezember 2008 um 21:29

[...] will hier nicht nochmal Selenium vorstellen. Das haben andere schon viel früher gemacht. Mit geht es viel mehr darum, kurz eine nette Eigenschaft von Selenium erwähnen, weswegen dieses [...]

RSS für Kommentare zu diesem Artikel · TrackBack URI

Schreib Deine Meinung