Testing
-
Auf Arbeit war bei mir das Testen nie ein Thema. Chef will Ergebnis haben und schreibt nicht vor, wie ich dazu komme.
Ich teste eigentlich nur den Code, den ich nicht verstehe. Ja, das kommt schonmal vor, irgendwie kann ich manchmal Code schreiben, der funktioniert, und den ich nicht verstehe.
Aber meine Erfahrung ist, daß es gar keine Mehr-Fehler findet, wenn ich den ganzen Code mit Unit-Tests vollhaue.
Was ich aber festgestellt habe, ist, daß ich immer eine offensichtlich korrekte sofort verstehbare Kopie als Kommentar im Code belassen sollte, wenn ich anfange, eine Sache zu optimieren, damit ich die Klar-Version einschalten kann, wenn in der Nähe was ist, um zu sehen, ob's an der Funktion oder an deren Eingaben lag.
Ich habe praktisch nie nichtreproduzierbare Fehler, wie sie zum Beispiel in C++ gerne kommen, wenn man sich nicht an den Standard hält. Wenn der Fehler einmal fliegt, kann ich ihn wiederholen und fangen.
Testläufe, also Eingabe- und dazupassende Ausgabefolgen definiere ich gelegentlich zum Spaß nebst dazugehörigem Testprogramm. Aber gefunden habe ich damit noch nie was. Ich war nur "sicherer", daß alles ok ist, völlig irrational. Mein Hausmeister würde sagen "Davon, daß die Sau täglich gewogen wird, wird sie auch nicht fetter."
-
Ich finde Unit-Test unverzichtbar. Für produktiven Code (so lange es sich nicht um Eintagsfliegen handelt) möchte ich grundsätzlich Unit-Tests schreiben. Und schreibe sie auch - wenn man mich lässt. Ich glaube, das Vorhandensein von Unit-Tests ist ein Teil der Unternehmenskultur und testgetriebenes Programmieren schreint mir unverzichtbar.
Bei der Frage, ob man mehr Fehler findet, ist meine Erfahrung anders als deine, volkard. Durch Unit-Tests finde ich mehr Fehler als ohne, ich finde sie schneller und zuverlässiger. Außerdem sind Änderungen (z.B. Refactoring) leichter und schneller durchführbar. Sie führen zu deutlich weniger Fehlern im Gesamtprojekt.
Außerdem gibt's noch eine psychologische Komponente: Nach meiner Erfahrung hat man einfach mehr Zutrauen in den Code, wenn ausreichende Unit-Test vorhanden sind. Änderungen führt man unter diesen Bedingungen leichteren Herzens durch. Man traut sich einfach mehr.
Meine Erfahrungen mit Akzeptanz-Tests sind nicht so gut. Vielleicht sind diese Tests einfach zu langweilig - kann ja sein. Jedenfalls werden hier kaum noch Fehler gefunden, die gibt's dann halt in der Produktion.
Stefan.
-
DStefan schrieb:
...
Hast Du das auch mal in so weit gebracht, daß die Tests vor der Software waren? Also Test-Driven-Develpment? Falls ja, wie war es?
-
volkard schrieb:
Auf Arbeit war bei mir das Testen nie ein Thema. Chef will Ergebnis haben und schreibt nicht vor, wie ich dazu komme.
Ich teste eigentlich nur den Code, den ich nicht verstehe. Ja, das kommt schonmal vor, irgendwie kann ich manchmal Code schreiben, der funktioniert, und den ich nicht verstehe.
Aber meine Erfahrung ist, daß es gar keine Mehr-Fehler findet, wenn ich den ganzen Code mit Unit-Tests vollhaue.
Was ich aber festgestellt habe, ist, daß ich immer eine offensichtlich korrekte sofort verstehbare Kopie als Kommentar im Code belassen sollte, wenn ich anfange, eine Sache zu optimieren, damit ich die Klar-Version einschalten kann, wenn in der Nähe was ist, um zu sehen, ob's an der Funktion oder an deren Eingaben lag.
Ich habe praktisch nie nichtreproduzierbare Fehler, wie sie zum Beispiel in C++ gerne kommen, wenn man sich nicht an den Standard hält. Wenn der Fehler einmal fliegt, kann ich ihn wiederholen und fangen.
Testläufe, also Eingabe- und dazupassende Ausgabefolgen definiere ich gelegentlich zum Spaß nebst dazugehörigem Testprogramm. Aber gefunden habe ich damit noch nie was. Ich war nur "sicherer", daß alles ok ist, völlig irrational. Mein Hausmeister würde sagen "Davon, daß die Sau täglich gewogen wird, wird sie auch nicht fetter."Wie groß waren die Projekte? Hast du nur allein daran gearbeitet, so dass du davon ausgehen kannst, dass kein andere deine Funktionen verschlimmbessert?
Unit tests sind vorallem gut, wenn mehrere Leute am gleichen Code arbeiten.
-
asdfgasdfg schrieb:
Hast du nur allein daran gearbeitet
Meistens allein oder als Anführer von ein paar Nubes.
asdfgasdfg schrieb:
so dass du davon ausgehen kannst, dass kein andere deine Funktionen verschlimmbessert?
Darf der das? Beim Teamwork habe ich eigentlich nie fremde Sachen "repariert", höchstens mal lokal und temporär.
-
Definitiv so viel wie möglich Unittesten. In meinen Augen funktioniert testen nur dann gut, wenn man es ohne viel Aufwand immer wieder durchführen kann. Und was könnte einfacher sein, als auf einen Knopf zu drücken, kurz zu warten und dann zu gucken ob die Anzeige rot oder grün ist?
Meiner Erfahrung nach wird am Testen immer als erstes gespart. Deswegen muss Testen so einfach wie möglich sein. Gibts keine automatisierten Tests, traut sich viel selten jemand, größere Refactorings durchzuführen. Das Resultat ist schlechterer Code.
Häufig ist es doch so, dass man Programmteil A schreibt, das man für sich alleine recht einfach manuell testen kann. Dann schreibt man Programmteil B. Der lässt sich auch noch leicht testen. Dann kommt Teil C hinzu. Der benutzt Teile von A und B. Das ist dann schon schwieriger zu testen. Dann ändert jemand was an A. Dann muss man auch C testen, denn es könnte sein, dass A noch funktioniert aber C nicht mehr. Und so gehts immer weiter. Mit automatischen Unittests kann man problemlos einen Teil der Anwendung modifizieren und kann sofort überprüfen, ob der Rest der Anwendung dann auch noch läuft. Zumindest wenn die Unittests von guter Qualität sind.
Und im Idealfall sind die Unittests dann noch durch Continous Integration fest in den Entwicklungsprozess verwoben.
-
volkard schrieb:
DStefan schrieb:
...
Hast Du das auch mal in so weit gebracht, daß die Tests vor der Software waren? Also Test-Driven-Develpment? Falls ja, wie war es?
Ja. Ich habe es sogar selbst initiiert. Also die Einführung von Unit-Tests und die testgesteuerte Arbeitsweise.
Und es war scheußlich, also die Arbeitsweise, nicht die Tests an sich. Zeitverschwendung. Dumm. Frustierend. Was du willst. Ich hab's nicht gemocht und keiner aus dem Team mochte es.
Aber ich hatte mich schon zu weit aus dem Fenster gelehnt, um einfach damit aufzuhören. Wir hatten bereits Richtlinien verfasst und als Team abgesegnet. Also mussten wir weiter machen. Und nach einer gewissen Zeit fanden wir hinein (manche besser als andere). Es begann Spaß zu machen und sich "natürlich" anzufühlen.
Heute favorisiere ich eine Arbeitsweise, die nicht stur nach diesem Rezept vorgeht. Ich schreibe meist nur ein paar (vielleicht 50 Prozent?) der Test vor dem eigentlichen Code, der Rest kommt dann später. Es scheint eine Schmerzschwelle zu geben. Wenn die Tests nicht innerhalb von zwei oder drei Arbeitstagen laufen (manche Funktionen arbeiten ja erst korrekt, wenn man eine Menge anderer Funktionen laufen hat), wird es wieder frustrierend. Wenn ich kann, trage ich dem Rechnung, indem ich bestimmte Funktionen zwar implementiere (etwa um Schnittstellen darzulegen) aber zunächst nicht teste.
Wichtig scheint mir zu sein, dass es am Ende, also spätestens wenn man den Code frei gibt, Unit-Tests gibt, die alle relevanten Teile des Codes abdecken.
Stefan.
-
DStefan schrieb:
Wenn die Tests nicht innerhalb von zwei oder drei Arbeitstagen laufen (manche Funktionen arbeiten ja erst korrekt, wenn man eine Menge anderer Funktionen laufen hat), wird es wieder frustrierend. Wenn ich kann, trage ich dem Rechnung, indem ich bestimmte Funktionen zwar implementiere (etwa um Schnittstellen darzulegen) aber zunächst nicht teste.
Wichtig scheint mir zu sein, dass es am Ende, also spätestens wenn man den Code frei gibt, Unit-Tests gibt, die alle relevanten Teile des Codes abdecken.
Wenn ein Test erst die Fertigstellung von anderen Programmteilen erfordert oder man für einen Test mehrere Tage braucht, dann handelt es sich aber nicht mehr um Unittests und man sollte das eigene Testkonzept nochmal überdenken. Ein Unittest ist zum Testen einer (kleinsten) Einheit, also z.B. einer einzelnen Methode oder Funktion. Wenn man dafür erst andere Programmteile benötigt, dann sollte man diesen Mocken.
Was Du da beschreibst, klingt eher nach Integrationstests. Und auch die sollten nicht mehrere Tage Arbeit kosten.
-
byto schrieb:
DStefan schrieb:
Wenn die Tests nicht innerhalb von zwei oder drei Arbeitstagen laufen (manche Funktionen arbeiten ja erst korrekt, wenn man eine Menge anderer Funktionen laufen hat), wird es wieder frustrierend. Wenn ich kann, trage ich dem Rechnung, indem ich bestimmte Funktionen zwar implementiere (etwa um Schnittstellen darzulegen) aber zunächst nicht teste.
Wichtig scheint mir zu sein, dass es am Ende, also spätestens wenn man den Code frei gibt, Unit-Tests gibt, die alle relevanten Teile des Codes abdecken.
Wenn ein Test erst die Fertigstellung von anderen Programmteilen erfordert oder man für einen Test mehrere Tage braucht, dann handelt es sich aber nicht mehr um Unittests und man sollte das eigene Testkonzept nochmal überdenken. Ein Unittest ist zum Testen einer (kleinsten) Einheit, also z.B. einer einzelnen Methode oder Funktion. Wenn man dafür erst andere Programmteile benötigt, dann sollte man diesen Mocken.
Was Du da beschreibst, klingt eher nach Integrationstests. Und auch die sollten nicht mehrere Tage Arbeit kosten.
Ich rede nicht von der Erstellung der Tests, sondern von der Erstellung des Codes, der getestet wird. Und hier kann es vorkommen, dass man zur Implentierung einer einzigen Methode einer Klasse Tage braucht. Das ist doch nichts Besonderes!
Sorry, aber Unit-Tests sind zum Testen aller und nicht bloß der kleinsten Einheiten da. Wenn eine Einheit über andere Einheiten implementiert ist, dann ist der Code, der diese "umschließende" Einheit testet, ein Unit-Test. Warum auch nicht? Es kommt doch nicht auf die Größe an, damit etwas ein Unit-Test ist!
Stefan.
-
DStefan schrieb:
Und hier kann es vorkommen, dass man zur Implentierung einer einzigen Methode einer Klasse Tage braucht.
Darf ich fragen, was so eine Methode dann macht (mir ist bis jetzt nocht nichts so aufwändiges unter die Finger gekommen)?
-
Badestrand schrieb:
DStefan schrieb:
Und hier kann es vorkommen, dass man zur Implentierung einer einzigen Methode einer Klasse Tage braucht.
Darf ich fragen, was so eine Methode dann macht (mir ist bis jetzt nocht nichts so aufwändiges unter die Finger gekommen)?
void* CoolAllocator::alloc(size_t) wäre so ein fall.
binary_search natürlich.
md5, gzip
is_prime_number, rand, je nach qualitätsanspruch
also vorkommen kann's.
-
Herrmann schrieb:
wie läuft bei euch auf Arbeit das Testen von Programmen und Programmteilen ab?
Meistens so, dass es dem Programmierer überlassen ist, wie er es macht. Einmal durfte ich an einem Projekt teilnehmen, wo Testcode Pflicht war und es gab eine Vorgabe, wieviel Prozent vom Code beim Testen abgedeckt sein sollten (80% oder so, glaube ich).
In letzter Zeit schreibe ich relativ viel Testcode, aber nicht, weil ich aufs Testen scharf bin, sondern, weil ich u.a. wissen will, wieviele CPU Takte meine Funktionen benötigen und ob der Code mit allen Compiler Optimierungsstufen funktioniert...
-
Badestrand schrieb:
DStefan schrieb:
Und hier kann es vorkommen, dass man zur Implentierung einer einzigen Methode einer Klasse Tage braucht.
Darf ich fragen, was so eine Methode dann macht (mir ist bis jetzt nocht nichts so aufwändiges unter die Finger gekommen)?
Mitarbeiter::calcResturlaub()
Unter Berücksichtigung von:
- Wechselnden Arbeitsverträgen mit unterschiedlichen Urlaubsansprüchen im Betrachungszeitraum.
- Übernahme von Urlaub aus Vorjahren, aber nur, falls ein Antrag vorliegt und genehmigt wurde.
- Automatischem Inkrement des Anspruchs nach Betriebszugehörigkeit, wobei die Betriebszugehörigkeit, der maximale Anspruch und die Höhe des Inkrements von einem Dutzend (oder so) Bedingungen abhängen.
- Bereits genommenem Urlaub
- Krankheitstagen innerhalb des bereits genommenem Urlaubs.
- usw.
Dieses Beispiel fällt mir spontan ein. Wobei hier nicht bloß die Implementierung des produktiven Codes, sondern auch der Tests verdammt viele Tage in Anspruch genommen haben.
Dies ist übrigens auch ein Beispiel für eine Funktion, die ich um ungefähr 500% unterschätzt hatte
Stefan.
-
DStefan schrieb:
Mitarbeiter::calcResturlaub()
Unter Berücksichtigung von:
- Wechselnden Arbeitsverträgen mit unterschiedlichen Urlaubsansprüchen im Betrachungszeitraum.
- Übernahme von Urlaub aus Vorjahren, aber nur, falls ein Antrag vorliegt und genehmigt wurde.
- Automatischem Inkrement des Anspruchs nach Betriebszugehörigkeit, wobei die Betriebszugehörigkeit, der maximale Anspruch und die Höhe des Inkrements von einem Dutzend (oder so) Bedingungen abhängen.
- Bereits genommenem Urlaub
- Krankheitstagen innerhalb des bereits genommenem Urlaubs.
- usw.
Dieses Beispiel fällt mir spontan ein. Wobei hier nicht bloß die Implementierung des produktiven Codes, sondern auch der Tests verdammt viele Tage in Anspruch genommen haben.
Dies ist übrigens auch ein Beispiel für eine Funktion, die ich um ungefähr 500% unterschätzt hatte
Sie ruft also so um die 10 Hilfsfunktionen auf, die die Daten zusammentragen und Zwischenergebnisse berechnen?
-
volkard schrieb:
DStefan schrieb:
Mitarbeiter::calcResturlaub()
Unter Berücksichtigung von:
- Wechselnden Arbeitsverträgen mit unterschiedlichen Urlaubsansprüchen im Betrachungszeitraum.
- Übernahme von Urlaub aus Vorjahren, aber nur, falls ein Antrag vorliegt und genehmigt wurde.
- Automatischem Inkrement des Anspruchs nach Betriebszugehörigkeit, wobei die Betriebszugehörigkeit, der maximale Anspruch und die Höhe des Inkrements von einem Dutzend (oder so) Bedingungen abhängen.
- Bereits genommenem Urlaub
- Krankheitstagen innerhalb des bereits genommenem Urlaubs.
- usw.
Dieses Beispiel fällt mir spontan ein. Wobei hier nicht bloß die Implementierung des produktiven Codes, sondern auch der Tests verdammt viele Tage in Anspruch genommen haben.
Dies ist übrigens auch ein Beispiel für eine Funktion, die ich um ungefähr 500% unterschätzt hatte
Sie ruft also so um die 10 Hilfsfunktionen auf, die die Daten zusammentragen und Zwischenergebnisse berechnen?
Ungefähr, ja. Willst du darauf hinaus, dass man die Hilfsfunktionen getrennt hätte testen können? Die meisten waren private, so dass das nicht ging.
Stefan.
-
Die meisten waren private, so dass das nicht ging.
#define private public
-
DStefan schrieb:
volkard schrieb:
Sie ruft also so um die 10 Hilfsfunktionen auf, die die Daten zusammentragen und Zwischenergebnisse berechnen?
Ungefähr, ja. Willst du darauf hinaus, dass ...
Gar nicht. Ich wollte den vagen Eindruck geklärt haben, ob Du da einen 500-Zeiler gebaut hast.
Man hätte durch
Und hier kann es vorkommen, dass man zur Implentierung einer einzigen Methode einer Klasse Tage braucht.
den Eindruck gewinnen können.
-
DStefan schrieb:
Sorry, aber Unit-Tests sind zum Testen aller und nicht bloß der kleinsten Einheiten da. Wenn eine Einheit über andere Einheiten implementiert ist, dann ist der Code, der diese "umschließende" Einheit testet, ein Unit-Test. Warum auch nicht? Es kommt doch nicht auf die Größe an, damit etwas ein Unit-Test ist!
Angenommen Du hast Einheit A, B und C. Einheit C benutzt Einheit B und Einheit B benutzt Einheit A. Du schreibst einen Unittest um Einheit C zu testen. Damit testest Du implizit auch A und B. Nun gibts irgendwann Änderungen und der Test zu C schlägt fehl. Du weisst nun zwar, dass irgendwo ein Fehler ist, siehst aber nicht umbedingt auf den ersten Blick, ob der Fehler in A, B oder C liegt.
Deswegen ist der Sinn von Unittests eben doch, die kleinsten Einheiten eines Programms zu testen. Das interessante daran ist, dass solche Unittests nicht nur helfen, Fehler schneller zu finden und zu beheben. Sie helfen sogar dabei, das Programm besser zu strukturieren! Es hilft dabei Seiteneffekte zwischen Programmteilen zu verhindern, weil sich diese eben isoliert nur schwer testen lassen.
The goal of unit testing is to isolate each part of the program and show that the individual parts are correct.
-
volkard schrieb:
DStefan schrieb:
volkard schrieb:
Sie ruft also so um die 10 Hilfsfunktionen auf, die die Daten zusammentragen und Zwischenergebnisse berechnen?
Ungefähr, ja. Willst du darauf hinaus, dass ...
Gar nicht. Ich wollte den vagen Eindruck geklärt haben, ob Du da einen 500-Zeiler gebaut hast.
Man hätte durch
Und hier kann es vorkommen, dass man zur Implentierung einer einzigen Methode einer Klasse Tage braucht.
den Eindruck gewinnen können.
Oh nein! 500-Zeiler kommen mir nicht ins Haus. Jedenfalls nicht in produktivem Code. Wenn man z.B. eine neue Lib ausprobiert schreibt man schonmal eher, wie einem der Schnabel gewachsen ist. Ob du's glaubst oder nicht, ich bin ein Code-Ästhet
Stefan.
-
byto schrieb:
DStefan schrieb:
Sorry, aber Unit-Tests sind zum Testen aller und nicht bloß der kleinsten Einheiten da. Wenn eine Einheit über andere Einheiten implementiert ist, dann ist der Code, der diese "umschließende" Einheit testet, ein Unit-Test. Warum auch nicht? Es kommt doch nicht auf die Größe an, damit etwas ein Unit-Test ist!
Angenommen Du hast Einheit A, B und C. Einheit C benutzt Einheit B und Einheit B benutzt Einheit A. Du schreibst einen Unittest um Einheit C zu testen. Damit testest Du implizit auch A und B. Nun gibts irgendwann Änderungen und der Test zu C schlägt fehl. Du weisst nun zwar, dass irgendwo ein Fehler ist, siehst aber nicht umbedingt auf den ersten Blick, ob der Fehler in A, B oder C liegt.
Deswegen ist der Sinn von Unittests eben doch, die kleinsten Einheiten eines Programms zu testen. Das interessante daran ist, dass solche Unittests nicht nur helfen, Fehler schneller zu finden und zu beheben. Sie helfen sogar dabei, das Programm besser zu strukturieren! Es hilft dabei Seiteneffekte zwischen Programmteilen zu verhindern, weil sich diese eben isoliert nur schwer testen lassen.
Du hast Recht, wenn du sagst, dass Unit-Tests helfen, Programme besser zu strukturieren. Oder zumindest helfen können, denn natürlich kann man auch mit Unit-Tests den größten Mist verzapfen.
Den ersten Teil deines Beitrags aber finde ich nicht plausibel. Natürlich müssen A und B getestet werden. Wenn aber C die beiden anderen Teile verwendet, muss es auch für C einen Unit-Test geben.
Denn erstens kann C ja die Teile falsch verwenden, und es ist denkbar, dass A und B für sich genommen zwar fehlerfrei sind, im Zusammenspiel aber nicht funktionieren. Ein "Kooperationsfehler" käme also nur im Test von C an's Licht.
Zweitens enthält C immer auch Code, und sei es nur (was nach meiner Erfahrung eher selten vorkommt) solchen Code, der die verwendeten Teile aktiviert (Variablen anlegen, Methoden von A und B aufrufen). Deshalb muss es auch dann für C einen Unit-Test geben, wenn C ausschließlich A und B verwendet.
Ich spreche mich hier ausdrücklich nicht für implizite Tests aus! Die braucht man nicht und die soll man auch nicht schreiben. Aber ich sehe keinen Grund dafür, nur kleinste Einheiten per Unit-Tests zu testen.
Übrigens: Was ist eigentlich eine "kleinste Einheit"? Nur eine, die selbst keine anderen Einheiten verwendet?
Stefan.