it-swarm.com.de

Sollte ich meine Unterklassen oder meine abstrakte Elternklasse einem Unit-Test unterziehen?

Ich habe eine Skelettimplementierung, wie in Punkt 18 von Effective Java (erweiterte Diskussion hier ). Es ist eine abstrakte Klasse, die zwei öffentliche Methoden methodA () und methodB bereitstellt () die Unterklassenmethoden aufrufen, um "die Lücken zu füllen", die ich nicht abstrahiert definieren kann.

Ich habe es zuerst entwickelt, indem ich eine konkrete Klasse erstellt und Unit-Tests dafür geschrieben habe. Als die zweite Klasse kam, konnte ich allgemeines Verhalten extrahieren, die "fehlenden Lücken" in der zweiten Klasse implementieren und es war fertig (natürlich wurden die Komponententests für die zweite Unterklasse erstellt).

Die Zeit verging und jetzt habe ich 4 Unterklassen, von denen jede 3 kurze geschützte Methoden implementiert, die sehr spezifisch für ihre konkrete Implementierung sind, während die Skelettimplementierung die gesamte generische Arbeit erledigt.

Mein Problem ist, dass ich beim Erstellen einer neuen Implementierung die Tests erneut schreibe:

  • Ruft die Unterklasse eine bestimmte erforderliche Methode auf?
  • Hat eine bestimmte Methode eine bestimmte Anmerkung?
  • Erhalte ich die erwarteten Ergebnisse von Methode A?
  • Erhalte ich die erwarteten Ergebnisse von Methode B?

Während sehen Vorteile mit diesem Ansatz:

  • Es dokumentiert die Anforderungen der Unterklasse durch Tests
  • Bei einem problematischen Refactoring kann dies schnell fehlschlagen
  • Testen Sie die Unterklasse als Ganzes und über ihre öffentliche API ("funktioniert methodA ()?" Und nicht "funktioniert diese geschützte Methode?").

Das Problem, das ich habe, ist, dass die Tests für eine neue Unterklasse im Grunde genommen ein Kinderspiel sind: - Die Tests sind in allen Unterklassen alle gleich, sie ändern nur den Rückgabetyp der Methoden (die Skeleton-Implementierung ist generisch) und die Eigenschaften I. Überprüfen Sie den Assert-Teil des Tests. - Normalerweise haben die Unterklassen keine spezifischen Tests für sie

Ich mag die Art und Weise, wie sich die Tests auf das Ergebnis der Unterklasse selbst konzentrieren und es vor Refactoring schützen, aber die Tatsache, dass die Implementierung des Tests nur "manuelle Arbeit" wurde, lässt mich denken, dass ich etwas falsch mache.

Ist dieses Problem beim Testen der Klassenhierarchie häufig? Wie kann ich das vermeiden?

ps: Ich habe darüber nachgedacht, die Skelettklasse zu testen, aber es scheint auch seltsam, ein Modell einer abstrakten Klasse zu erstellen, um sie testen zu können. Und ich denke, dass ein Refactoring der abstrakten Klasse, das das Verhalten ändert, das von der Unterklasse nicht erwartet wird, nicht so schnell bemerkt wird. Mein Mut sagt mir, dass das Testen der Unterklasse "als Ganzes" der bevorzugte Ansatz ist, aber bitte zögern Sie nicht, mir zu sagen, dass ich falsch liege :)

ps2: Ich habe gegoogelt und viele Fragen zu diesem Thema gefunden. Einer von ihnen ist dieser der eine großartige Antwort von Nigel Thorne hat, mein Fall wäre seine "Nummer 1". Das wäre großartig, aber ich kann es derzeit nicht umgestalten, also müsste ich mit diesem Problem leben. Wenn ich umgestalten könnte, hätte ich jede Unterklasse als Strategie. Das wäre in Ordnung, aber ich würde immer noch die "Hauptklasse" und die Strategien testen, aber ich würde kein Refactoring bemerken, das die Integration zwischen Hauptklasse und Strategien unterbricht.

ps3: Ich habe einige Antworten gefunden, die besagen, dass es "akzeptabel" ist, die abstrakte Klasse zu testen. Ich bin damit einverstanden, dass dies akzeptabel ist, aber ich würde gerne wissen, welcher Ansatz in diesem Fall der bevorzugte ist (ich beginne immer noch mit Unit-Tests).

12
JSBach

Ich kann nicht sehen, wie Sie Tests für eine abstrakte Basisklasse schreiben würden. Sie können es nicht instanziieren, also können Sie keine Instanz davon haben to test. Sie könnten eine konkrete "Dummy" -Unterklasse nur zum Testen erstellen - Sie sind sich nicht sicher, wie viel Nutzen dies bedeuten würde. YMMV.

Jedes Mal, wenn Sie eine neue Implementierung (Klasse) erstellen, sollten Sie Tests schreiben, die diese neue Implementierung ausführen. Da Teile der Funktionalität (Ihre "Lücken") in der Unterklasse each implementiert sind, ist dies unbedingt erforderlich. Die Ausgabe jeder der geerbten Methoden kann (und wahrscheinlich sollte) für jede neue Implementierung unterschiedlich sein.

5
Phill W.

Normalerweise erstellen Sie eine Basisklasse, um eine Schnittstelle zu erzwingen. Sie möchten diese Schnittstelle testen, nicht die folgenden Details. Daher sollten Sie Tests erstellen, die jede Klasse einzeln instanziieren, aber nur mit den Methoden der Basisklasse testen.

Der Vorteil dieser Methode besteht darin, dass Sie die Schnittstelle jetzt testen können, da Sie sie getestet haben. Möglicherweise befindet sich der Code in einer untergeordneten Klasse im übergeordneten Code oder umgekehrt. Jetzt werden Ihre Tests beweisen, dass Sie das Verhalten beim Verschieben dieses Codes nicht unterbrochen haben.

Im Allgemeinen sollten Sie den Code testen, wie er verwendet werden soll. Das bedeutet, dass Sie das Testen privater Methoden vermeiden müssen, und häufig bedeutet dies, dass Ihre zu testende Einheit möglicherweise etwas größer ist als ursprünglich angenommen - möglicherweise eine Gruppe von Klassen anstelle einer einzelnen Klasse. Ihr Ziel sollte es sein, Änderungen zu isolieren, damit Sie selten einen Test ändern müssen, wenn Sie in einem bestimmten Bereich umgestalten.

4
Michael K