D
byto schrieb:
Aber wenn Du für obigen Teil Unittests für A, B und C schreibst, dann wird A implizit zweimal mitgetestet (von B und C) und B einmal (durch C). Besser wäre es, solche Seiteneffekte im Vorhinein zu verhindern. Sind A, B und C frei von Seiteneffekten, können sie unabhängig voneinander getestet werden. Die Integration von A, B und C kann dann über Integrationstests (siehe Unterschied zu Unittests!!) getestet werden.
Häufig kann man Seiteneffekte nicht gänzlich verhindern. Aber auch dafür gibts ne Möglichkeit, nämlich Mock-Objekte. Durch Mock-Objekte kann man das mehrfache implizite Testen immer gleicher Programmteile verhindern. Das ist vor allem in größeren Projekten mehr als sinnvoll, weil man dadurch die Ausführungsgeschwindigkeit der Tests minimieren kann.
Entschuldige byto, aber das klingt doch ziemlich verrückt!
Nach deiner ursprünglichen Definition ist Einheit C implementiert über die Einheit B und diese wiederum über Einheit A. Du schreibst weiter, dass ein Test von Einheit C "implizit" die Einheiten A und B testet - und das hältst du für falsch. Nach deiner Definition dürfte man somit ausschließlich einen Unit-Test für Einheit A schreiben, denn dies ist eine "kleinste Einheit". Einheit B testet schließlich auch "implizit" - nämlich Einheit A.
Du siehst selbst, wohin das führt und forderst deshalb, solche "Seiteneffekte" zu vermeiden. Wo das nicht geht, möchtest du "Mock Objekte" einsetzen.
Zunächst einmal ist der Begriff "Seiteneffekt" in diesem Zusammenhang nicht sehr glücklich gewählt. Üblicherweise bezeichnet man als Seiteneffekt etwas ganz andres. Bleiben wir lieber bei "impliziter Test".
Dann deine Schussfolgerung oder Behauptung, durch den Test von C würden A und B implizit mit getestet. Das stimmt meines Erachtens doch gar nicht. Oder wenigstens nicht zwangsläufig. Der Code von A und B wird ausgeführt, das ist richtig. Aber wenn man's richtig macht, wird dieser Code im Test von C nicht getestet! Er muss halt nur ausgeführt werden, damit C funktioniert.
Auch was die Mock-Objekte angeht, stimme ich nicht mit dir überein. Mock-Objekte verwendet man, um in Unit-Tests langwierige oder während der Tests nicht verfügbare Operationen zu simulieren. Man kann sie auch verwenden, um Tests reproduzierbar zu machen. In allen Fällen aber bieten Mock-Objekte einen simulierten Ausschnitt der Umgebung der zu testenden Software. Das können Datenbank-Zugriffe sein (weil z.B. der Status der Daten nicht unter der Kontrolle des Testers liegt) oder Zugriffe auf externe Software (etwa Web-Services, die bestimmte Daten liefern). Ich habe auch schon Mock-Objekte geschrieben, die einen VoIP-Dialer simulieren - schließlich will man ja nicht, dass bei jedem Unit-Test irgendwelche Leute angerufen werden
Du schlägst vor, man solle Mock-Objekte auch verwenden, um "Seiteneffekte" beim Testen, also "implizites" Testen zu vermeiden. Aber dazu müsste man doch beim Test von C die Einheiten B und A entfernen, so dass MockB und MockA zum Einsatz kommen! C muss also auf den Unit-Test eigens vorbereitet werden! Und das alles nur, um zu verhindern, dass beim Test von C Code in A und B ausgeführt wird, der ja bereits getestet ist. Ich sage dir, um sowas zu tun bräuchte ich schon einen verdammt guten Grund. Und die von dir vorgestellte "reine Lehre" des Unit-Testings ist aus meiner Sicht überhaupt kein Grund.
Nun gut. Angenommen, wir hätten jetzt C dazu gebracht, im Unit-Test statt B und A MockB und MockA zu verwenden. Wir schreiben unseren Test und er läuft. Aber was haben wir jetzt getestet? (Das ist ein Problem, das immer auftritt, wenn man Mock-Objekte verwendet.) Wir haben getestet, dass C zum Beispiel MockB korrekt verwendet. Aber dies geschieht im Sinne von MockB und nicht im Sinne von B selbst. Alle relevanten Änderungen in B müssen peinlichst in MockB nachvollzogen werden, sonst ist unser Unit-Test von C nicht mehr korrekt. Wir nehmen unter Umständen eine Heidenarbeit auf uns, testen C nicht in seiner "natürlichen", sondern nur in einer idealisierten Umgebung, und das alles nur, um nicht mit C implizit auch A und B zu testen?
Mir geht das zu weit. Ich möchte, im Gegenteil, alle Unit-Tests möglichst so schreiben, dass sie den Code testen, der später auch prouktiv wird. Natürlich geht das nicht immer, wie ich oben schon sagte, aber ich finde, man sollte eine Menge dafür tun.
Stefan.