Inwieweit testen Sie interne / private Komponenten einer Klasse / eines Moduls / Pakets / usw.? Testen Sie sie überhaupt oder testen Sie nur die Schnittstelle zur Außenwelt? Ein Beispiel für diese internen Methoden sind private Methoden.
Stellen Sie sich als Beispiel einen rekursiven Descent-Parser vor , der mehrere interne Prozeduren (Funktionen / Methoden) enthält, die von einer zentralen Prozedur aufgerufen werden. Die einzige Schnittstelle zur Außenwelt ist die zentrale Prozedur, die eine Zeichenfolge verwendet und die analysierten Informationen zurückgibt. Die anderen Prozeduren analysieren verschiedene Teile der Zeichenfolge und werden entweder von der zentralen Prozedur oder von anderen Prozeduren aufgerufen.
Natürlich sollten Sie die externe Schnittstelle testen, indem Sie sie mit Beispielzeichenfolgen aufrufen und mit der von Hand analysierten Ausgabe vergleichen. Aber was ist mit den anderen Verfahren? Würden Sie sie einzeln testen, um zu überprüfen, ob sie ihre Teilzeichenfolgen korrekt analysieren?
Ich kann mir ein paar Argumente vorstellen:
Vorteile :
- Mehr Tests sind immer besser, und dies kann dazu beitragen, die Codeabdeckung zu erhöhen
- Bei einigen internen Komponenten ist es möglicherweise schwierig, bestimmte Eingaben (z. B. Flankenfälle) über die externe Schnittstelle vorzunehmen
- Klarere Prüfung. Wenn eine interne Komponente einen (behobenen) Fehler aufweist, wird durch einen Testfall für diese Komponente deutlich, dass sich der Fehler in dieser bestimmten Komponente befand
Nachteile :
- Refactoring wird zu schmerzhaft und zeitaufwändig. Um etwas zu ändern, müssen Sie die Komponententests neu schreiben, auch wenn die Benutzer der externen Schnittstelle nicht betroffen sind
- Einige Sprachen und Test-Frameworks erlauben dies nicht
Was sind deine Meinungen?
quelle
Antworten:
Fall: Ein "Modul" (im weitesten Sinne, dh etwas mit einer öffentlichen Schnittstelle und möglicherweise auch einigen privaten inneren Teilen) enthält eine komplizierte / involvierte Logik. Wenn nur die Modulschnittstelle getestet wird, handelt es sich um eine Art Integrationstest in Bezug auf die innere Struktur des Moduls. Wenn also ein Fehler festgestellt wird, lokalisiert dieser Test nicht das genaue Innenteil / die Komponente, die für den Fehler verantwortlich ist.
Lösung: Machen Sie die komplizierten Innenteile selbst zu Modulen, testen Sie sie in einer Einheit (und wiederholen Sie diese Schritte für sie, wenn sie selbst zu kompliziert sind) und importieren Sie sie in Ihr Originalmodul. Jetzt haben Sie nur eine Reihe von Modulen, die einfach genug sind, um sie zu testen (beide prüfen, ob das Verhalten korrekt ist und beheben Fehler), und das ist alles.
Hinweis:
Es ist nicht erforderlich, Änderungen an den Tests der (früheren) "Untermodule" des Moduls vorzunehmen, wenn der Vertrag des Moduls geändert wird, es sei denn, das Angebot des "Untermoduls" reicht nicht mehr aus, um den neuen / geänderten Vertrag zu erfüllen.
nichts unnötig gemacht werden öffentlich dh der Vertrag des Moduls gehalten werden und die Einkapselung gehalten.
[Aktualisieren]
So testen Sie eine intelligente interne Logik in Fällen, in denen es schwierig ist, die inneren Teile des Objekts (ich meine Mitglieder, nicht die privat importierten Module / Pakete) in einen geeigneten Zustand zu versetzen, indem Sie sie einfach über die öffentliche Schnittstelle des Objekts eingeben:
Lassen Sie einfach Code mit friend (in C ++) oder package (Java) auf die Innereien testen, setzen Sie den Status von innen und testen Sie das Verhalten, wie Sie möchten.
quelle
[assembly: InternalsVisibleTo("MyUnitTestAssembly")]
Attribut in your verwendenAssemblyInfo.cs
, um Interna zu testen. Es fühlt sich an wie Betrug.Der Ansatz für FSM-basierten Code unterscheidet sich ein wenig von dem traditionell verwendeten. Es ist sehr ähnlich zu dem, was hier für Hardware-Tests beschrieben wird (was normalerweise auch ein FSM ist).
Kurz gesagt, Sie erstellen eine Testeingabesequenz (oder eine Reihe von Testeingabesequenzen), die nicht nur eine bestimmte Ausgabe erzeugen soll, sondern auch bei der Erzeugung einer bestimmten "schlechten" Ausgabe die Identifizierung der ausgefallenen Komponente anhand der Art des Fehlers ermöglicht. Der Ansatz ist gut skalierbar. Je mehr Zeit Sie für das Testdesign aufwenden, desto besser wird der Test.
Diese Art des Testens kommt den sogenannten "Funktionstests" näher, macht es jedoch überflüssig, die Tests jedes Mal zu ändern, wenn Sie eine Implementierung leicht berühren.
quelle
Es hängt davon ab :-). Wenn Sie einem BDD-Ansatz (Behavior Driven Development) oder einem ATDD-Ansatz (Acceptance Test Driven Development) folgen, ist das Testen der öffentlichen Schnittstelle in Ordnung (solange Sie sie ausführlich mit unterschiedlichen Eingaben testen. Die zugrunde liegende Implementierung, z. B. private Methoden, ist dies nicht eigentlich wichtig.
Nehmen wir jedoch an, Sie möchten, dass ein Teil dieses Algorithmus innerhalb eines bestimmten Zeitrahmens oder entlang einer bestimmten bigO-Kurve (z. B. nlogn) ausgeführt wird, und testen dann die einzelnen Teile. Einige würden dies eher als traditionellen TDD / Unit-Test-Ansatz bezeichnen.
Wie bei allem, YMMV
quelle
Spaltet es in mehrere Teile mit einer funktionellen Bedeutung, zum Beispiel
ParseQuotedString()
,ParseExpression()
,ParseStatement()
,ParseFile()
und sie alle öffentlich. Wie wahrscheinlich ist es, dass sich Ihre Syntax so stark ändert, dass diese irrelevant werden?quelle