HWK hat geschrieben:@poker: Bedeutet das, dass durch Unittests jede Zeile des Programmcodes mindestens einmal ausgeführt werden soll? Ist das nicht ein bisschen aufwendig und in vielen Fällen auch unnötig?
Hi, das ist ne gute berechtigte Frage.
Ich sehe es so wie BlackJack und Eydu.
Der am Unit tests gekoppelte Coverage soll dir als Programmierer in erste Linie die
lästige Arbeit abnehmen zu identifizieren, ob du alle benötigten Fälle (Die du für wichtig erachtest) abgedeckt hast. Bei sehr großen Anwendungen vergisst man immer wider mal für wichtige Bereiche seiner Software Unit tests zu schrieben, weil es eben Umfangreich, komplex und unübersichtlich wird[1].
Der Coverage soll dir also die Arbeit abnehmen die Bereiche zu identifizieren die noch nicht durch Unit tests angedeckt wurden[2]. Anschließend kannst du anhand des erzeugten Reports entscheiden, welche dingen deiner Meinung nach unbedingt abgedeckt werden müssen und welche für dich vernachlässigbar sind. Vernachlässigbare dingen könnten z.B. wirkliche Trivialfunktionen sein die ehe nicht mehr geändert werden und "immer" funktionieren. -- Aber wie BlackJack schon richtig angedeutet hat, der Teufel liegt im Detail
Oft sind es nämlich genau die dinge wo man denkt "braucht man nicht passt schon" und dort verstecken sich Fehler. Daher versuche ich meistens (je nach Komplexität der Software) eine 100%ige Abdeckung zu erreichen.
[1]: Und meistens ist es ja so das man Unit tests nicht parallel zur eigentlichen Software schreibt sondern erst viel später = Wochen oder gar Monate danach. Es sein den man nutzt
Testgetriebene Entwicklung, wo erst die Unit tests geschrieben werden und dann die jeweiligen Sachen implementiert werden.
[2]: Solche bereiche sind ja nicht nur innerhalb einer Funktion, sondern stellen den ganzen Module space dar: Sprich, auch Funktionen für die überhaupt noch kein Unit test existiert, werden dir durch den Coverage angezeigt. Also z.B. wenn ich Modul ``foobar`` habe in denn ``foo`` und ``bar`` ist und ich nur einen Unit test für ``foo`` geschrieben habe, zeigt der anschließende Coverage mir an, das ``bar`` vom unit test Modul nicht ausgeführt (=getestet) wurde.
---
Y0Gi hat geschrieben:
Achso, `py.test` nutze ich ebenfalls, da ich sehr schätze, dass es keine Vererbung voraussetzt und hübsch selbst alle Testmodule und -Callables findet. In irgendeinem Blog wurde es mit `unittest` und `nose` verglichen und `unittest` kam am besten weg
Ich schätze das war dann folgender Blog:
http://agiletesting.blogspot.com/2005/0 ... ttest.html
http://agiletesting.blogspot.com/2005/0 ... ctest.html
http://agiletesting.blogspot.com/2005/0 ... -tool.html
Y0Gi hat geschrieben:Ein paar (meinetwegen auch reallife-)Beispiele zum Testen von Datenbank- und Socket-Zugriffen würden mich brennend interessieren. Ich habe mir zwar die zahlreichen Python-Mock-Implementierungen sowie Ressourcen wie z.B. Googles Testing-Blog angesehen, aber so *richtig* komme ich da auf keinen grünen Zweig.
Die frage bei deinem Beispiel "Datenbank- und Socket-Zugriffen" ist, was genau deine Software macht? Wenn deine Software nur die Bestandteile nutzt (Die nicht von dir geschrieben sind), dann musst du die auch nicht testen! Es ist nämlich nicht deine Aufgabe auch fremde Libraries mit deinen Unit tests abzudecken. Dafür bittet es sich dann an Mocks zu schreiben.
Angenommen du hast eine Funktion oder Klasse die über `socket` auf sockets zugreift und in Abhängigkeit der Ergebnisse bestimmte Aktionen ausführt oder Zustände ändert. Jetzt kommt das Problem, das wenn du damit auf einen Externen $SERVER zugreifen der immer Bestimmte Daten zurückliefern muss. In dem Fall abstrahierst du `socket` und die erwarteten Daten von $SERVER in dem masse das deine Funktion genauso Arbeitet, als wenn du Tatsächlich `socket` mit $SERVER nutzen würdest.
Das heißt konkret, das du die
erforderlichen Schnittstellen von `socket` nachschreibst die sich halt stat sich mit $SERVER verbindet, eben nur die von dir von echten $SERVER erwarteten Daten zurückliefert. Das ganze ist dann zuzusagen ein Mock - Eine Attrappe - den du deiner Funktion unterschiebst. Natürlich musst du diese Attrappe soweit nachbauen das auch die von dir erwarteten Fehler Codes/Exception wie beim Original geschmissen werden.
Und genau hier fängt es an kompliziert zu werden. Das Thema ist wirklich nicht gerade trivial, und man kann da schnell an die Grenzen des machbaren (=Verhältnismäßigkeit) kommen. Nicht um sonst kann das schreiben einer Testumgebung schnelle mal umfangreicher und komplexer werden, als die zu testenden Umgebung. Hier muss man eben von Fall zu Fall zwischen Nutzen und Verhältnismäßigkeit abwägen.
Dan sollte man noch beachten ob die Software in einer Sicherheitsrelevanten Umgebung Einsatz findet. Dort sollte, IMO, sicherheitsrelevante Bereiche garnicht mit Mocks getestet werden, sondern eine Real existierende speziell dafür geschlafene Testumgebung eingerichtet werden.