Eine praktische Einführung in
Unit-Testing. Wie geht man da ran? Da ist bei mir der Zug irgendwann vorbeigefahren!
Und das gern im Zusammenhang mit Datenbankanwendungen.
Oder auch mit viel Oberfläche bzw. Benutzerinteraktion. Wie man das mit
Unit-Tests testet war mir immer ein Rätsel.
Tja, Sherlock, das kann ich sogar als nicht-mehr-Delphianer beantworten
Kurzum: Oberflächen testet man Prinzipbedingt schon nicht mit
Unit-Tests. Ein
Unit-Test testet per Definition eine kleinstmögliche Einheit (
Unit), das hat mit der Delphi-
Unit nichts gemeinsam. Ein einzelner
Unit-Test testet genau einen Effekt einer Methode. Hat die Methode mehrere Effekte, testet man jeden einzelnen mit einem einzelnen
Unit-Test. Man kann auch den Konstruktor als
Unit betrachten und dann den Initialzustand des Objektes überprüfen.
Sobald Du mehr als eine Klasse in einem Test abklopfst, bist Du schon aus der Welt der
Unit-Tests raus und in der Welt der Integration-Tests angekommen.
Das funktioniert anfangs noch so ähnlich wie
Unit-Tests - zumindest wenn alle am Test beteiligten Klassen noch in Deiner Kontrolle / in Deinem Projekt sind. Und solange man nicht weiter ausholt kann man die auch noch so schreiben wie die echten
Unit-Tests.
Sobald eine Klasse in Deinem Integration-Test dabei aber die Grenzen Deines SUT (System under Test) verlassen (Datenbankzugriffsklassen,
GUI-Klassen die native Apis callen wie die
VCL, Netzwerkzugriffsklassen), dann ist es mit der einfachen Testbarkeit vorbei, weil Du dann immer darauf achten musst, dass das externe System (die Datenbank, das Ding im Netzwerk auf das zugegriffen wird, das UI-System) vor jedem einzelnen Test in einen wohldefinierten Zustand gebracht wird.
Am Ende des Tages verbringst Du bei dieser Art zu testen mehr Zeit damit, externe Systeme zu managen und Code zum Vorbereiten der Tests zu schreiben, als eigentlicher Code und als Testcode (der bei normalen
Unit-Tests üblicherweise schon ein Vielfaches des zu testenden Codes beträgt). Insbesondere bei Datenbanken (herstellen der Test-
DB etc.) und bei Services (noch schlimmer wenn die auch ne
DB brauchen) ist man da gerne auf verlorenem Posten.
Tools wie z.B. TestComplete oder Ranorex können da leider auch nur bedingt helfen, und bringen alle ihre eigene Komplexität mit. Im Web siehts da ein klein bisschen besser aus, aber zum Trost auch nicht viel.
In der Praxis würde ich daher in den meisten Fällen vorschlagen, so viel wie möglich MVC zu fahren und dabei vor allem das M und den C sehr gut
Unit- und Integration zu testen. Bei der View wird dann ausschließlich auf Model-Binding gesetzt (genau gar keine Logik dort) und darauf vertraut, dass der Lieferant seine UI-Elemente selber vernünftig getestet hat, und das eigentliche UI gar nicht automatisiert zu testen. Das einzige was man dann nämlich mit dem UI-Test testen würde wäre, ob ein Wert im Model auch richtig angezeigt wird und Events vom UI richtig am Controller ankommen. Das hat aber mit der Programmlogik an sich nichts zu tun. Und die Programmlogik, die Testbar im Controller bzw. dahinter (Services, Repositories etc.) sitzen sollte, hat dann mit dem UI nichts mehr zu tun und kann wirklich intensiv und gut getestet werden.