Hi
Der Titel ist bewußt zweideutig
Es geht natürlich nicht um den Zweck von Unit-Tests, sondern um die Frage, für welche Klassen und Methoden und Konstellationen man wie Unit-Tests erstellt.
Die TDD-Verfechter werden jetzt sagen: Man schreibt Tests für alles, und das noch bevor man es entwickelt hat. Andere werden sagen: Man schreibt Tests für alles was wichtig ist
. Ich frage mich, wie man in der Praxis wirklich nützliche und sinnvolle systematische Unit-Tests erstellen kann. Bei Websuchen findet man immer diese Trivialbeispiele, die mit der Realität nichts zu tun haben (ich kann das Money-Beispiel nicht mehr sehen :noe: ).
Mal angenommen man wollte für so etwas vermeintlich triviales (!) wie das Java Collections Framework Unit-Tests schreiben. Eine handvoll recht überschaubarer Interfaces. Dann überlegt man und sucht vielleicht einen Ansatz, und schaut mal nach, ob es sowas nicht vielleicht schon gibt (ist ja nicht so abwegig, oder?) und findet dann z.B. in Guava eine abstrakte Basisklasse für den Test eher 'Map': MapInterfaceTest.java - google-collections - Google Collections Library - Google Project Hosting - ~650 (!) Zeilen stupider, repetitiver Trivialcode :noe: (Nur zum Vergleich: Die Implementierung in Form von HashMap hat (einschließlich ihrer abstrakten Basisklasse "AbstractMap"!) nur ca. 400 Zeilen...)
Damit das nicht in der ersten Antwort steht: Mir sind die Vorteile bewußt. Wenn man vorhat, mehrere verschiedene Map-Implementierungen zu schreiben ist man froh, wenn man so einen Test über die neue Implementierung drüberlaufen lassen kann, und sofort eventuelle Fehler findet. Und wenn man Änderungen macht, sieht man sofort, ob man vielleicht etwas kaputt gemacht hat. Es geht wie gesagt nicht um den eigentlichen Zweck von Unit-Tests. Der sollte jedem einleuchten.
Es geht eher darum, wie man das Ziel erreichen kann, systematisch Tests zu schreiben, wenn das Schreiben und Testen (!) solcher systematischer Tests so schnell so absurd aufwändig wird, wie schon in dem einfachen Map-Beispiel (und dann noch List, Set & Co dazu... Ich will mir gar nicht überlegen, was man noch in Test packen müßte, damit er seinen Namen wirklich verdient, wenn man z.B. spezifisches Verhalten von CopyOnWriteArrayList überprüfen wollte (multi-Threaded Unit-Tests?! :autsch: )). Duch die verschiedenen möglichen Aufrufreihenfolgen entsteht m.E. eine kombinatorische Explosion...
Vielleicht mal ganz konkret: Wer würde sich trauen, eine definitive Aussage darüber zu machen, ob der oben verlinkte Test eine 1:1-Entsprechung zu gültigen Map-Implementierungen darstellt? D.h. gibt es eine gültige Implemetierung, die den Test nicht passiert, oder eine ungültige Implementierung, die von dem Test nicht erkannt wird? :bahnhof: Wie soll man das herausfinden?
Oder sind wir vielleicht gerade im "Tal der Tränen", wo Software nicht mehr so einfach ist, dass man sie "per Hand" formal verifizieren kann, aber die Tools und Sprachfeatures zur formalen Defintion von Vor- und Nachbedingungen und Invarianten und dem automatischen Testen noch nicht praktisch einsetzbar sind, und man deswegen die Fleißarbeit auf sich nehmen muss, tausende Zeilen Testcode zu schreiben, der einen ein bißchen beruhigt, von dem man aber DOCH nie weiß, ob er seinen Zweck wirklich im strengsten Sinn erfüllt?
Bin gespannt auf die Antworten
Der Titel ist bewußt zweideutig
Die TDD-Verfechter werden jetzt sagen: Man schreibt Tests für alles, und das noch bevor man es entwickelt hat. Andere werden sagen: Man schreibt Tests für alles was wichtig ist
Mal angenommen man wollte für so etwas vermeintlich triviales (!) wie das Java Collections Framework Unit-Tests schreiben. Eine handvoll recht überschaubarer Interfaces. Dann überlegt man und sucht vielleicht einen Ansatz, und schaut mal nach, ob es sowas nicht vielleicht schon gibt (ist ja nicht so abwegig, oder?) und findet dann z.B. in Guava eine abstrakte Basisklasse für den Test eher 'Map': MapInterfaceTest.java - google-collections - Google Collections Library - Google Project Hosting - ~650 (!) Zeilen stupider, repetitiver Trivialcode :noe: (Nur zum Vergleich: Die Implementierung in Form von HashMap hat (einschließlich ihrer abstrakten Basisklasse "AbstractMap"!) nur ca. 400 Zeilen...)
Damit das nicht in der ersten Antwort steht: Mir sind die Vorteile bewußt. Wenn man vorhat, mehrere verschiedene Map-Implementierungen zu schreiben ist man froh, wenn man so einen Test über die neue Implementierung drüberlaufen lassen kann, und sofort eventuelle Fehler findet. Und wenn man Änderungen macht, sieht man sofort, ob man vielleicht etwas kaputt gemacht hat. Es geht wie gesagt nicht um den eigentlichen Zweck von Unit-Tests. Der sollte jedem einleuchten.
Es geht eher darum, wie man das Ziel erreichen kann, systematisch Tests zu schreiben, wenn das Schreiben und Testen (!) solcher systematischer Tests so schnell so absurd aufwändig wird, wie schon in dem einfachen Map-Beispiel (und dann noch List, Set & Co dazu... Ich will mir gar nicht überlegen, was man noch in Test packen müßte, damit er seinen Namen wirklich verdient, wenn man z.B. spezifisches Verhalten von CopyOnWriteArrayList überprüfen wollte (multi-Threaded Unit-Tests?! :autsch: )). Duch die verschiedenen möglichen Aufrufreihenfolgen entsteht m.E. eine kombinatorische Explosion...
Vielleicht mal ganz konkret: Wer würde sich trauen, eine definitive Aussage darüber zu machen, ob der oben verlinkte Test eine 1:1-Entsprechung zu gültigen Map-Implementierungen darstellt? D.h. gibt es eine gültige Implemetierung, die den Test nicht passiert, oder eine ungültige Implementierung, die von dem Test nicht erkannt wird? :bahnhof: Wie soll man das herausfinden?
Oder sind wir vielleicht gerade im "Tal der Tränen", wo Software nicht mehr so einfach ist, dass man sie "per Hand" formal verifizieren kann, aber die Tools und Sprachfeatures zur formalen Defintion von Vor- und Nachbedingungen und Invarianten und dem automatischen Testen noch nicht praktisch einsetzbar sind, und man deswegen die Fleißarbeit auf sich nehmen muss, tausende Zeilen Testcode zu schreiben, der einen ein bißchen beruhigt, von dem man aber DOCH nie weiß, ob er seinen Zweck wirklich im strengsten Sinn erfüllt?
Bin gespannt auf die Antworten