Ich versuche, TDD zu üben, indem ich es verwende, um einen einfachen Bit-Vektor zu entwickeln. Ich benutze Swift, aber das ist eine sprachunabhängige Frage.
My BitVector
ist ein struct
Speicherort für eine einzelne UInt64
Datei und bietet darüber eine API, mit der Sie sie wie eine Sammlung behandeln können. Die Details machen nicht viel aus, aber es ist ziemlich einfach. Die hohen 57 Bits sind Speicherbits und die unteren 6 Bits sind "Zählbits", die Ihnen mitteilen, wie viele der Speicherbits tatsächlich einen enthaltenen Wert speichern.
Bisher habe ich eine Handvoll sehr einfacher Fähigkeiten:
- Ein Initialisierer, der leere Bitvektoren erstellt
- Eine
count
Eigenschaft vom TypInt
- Eine
isEmpty
Eigenschaft vom TypBool
- Ein Gleichheitsoperator (
==
). NB: Dies ist einObject.equals()
in Java ähnlicher Wertegleichheitsoperator und kein Referenzgleichheitsoperator wie==
in Java.
Ich bin mit einer Reihe von zyklischen Abhängigkeiten konfrontiert:
Der Komponententest, der meinen Initialisierer testet, muss überprüfen, ob er neu erstellt wurde
BitVector
. Dies kann auf drei Arten geschehen:- Prüfen
bv.count == 0
- Prüfen
bv.isEmpty == true
- Prüfe das
bv == knownEmptyBitVector
Methode 1 stützt sich auf
count
Methode 2 stützt sich aufisEmpty
(die sich selbst stütztcount
, es macht also keinen Sinn, sie zu verwenden), Methode 3 stützt sich auf==
. In jedem Fall kann ich meinen Initialisierer nicht isoliert testen.- Prüfen
Der Test für
count
muss auf etwas funktionieren, was unweigerlich meine Initialisierer testetDie Umsetzung von
isEmpty
setzt aufcount
Die Umsetzung von
==
setzt aufcount
.
Ich konnte dieses Problem teilweise lösen, indem ich eine private API einführte, die BitVector
aus einem vorhandenen Bitmuster (als a UInt64
) ein erstellt. Dies ermöglichte es mir, Werte zu initialisieren, ohne andere Initialisierer zu testen, so dass ich meinen Weg nach oben "anschnallen" konnte.
Damit meine Komponententests wirklich Komponententests sind, mache ich eine Reihe von Hacks, die meinen Produkt- und Testcode erheblich komplizieren.
Wie genau gehen Sie mit solchen Problemen um?
quelle
BitVector
ist eine perfekte Größe für Unit-Tests und behebt sofort Ihre Probleme, die öffentliche MitgliederBitVector
gegenseitig benötigen, um aussagekräftige Tests durchzuführen.Antworten:
Sie machen sich zu viele Gedanken über Implementierungsdetails.
Es spielt keine Rolle , dass in der aktuellen Implementierung ,
isEmpty
stützt sich aufcount
(oder was auch immer andere Beziehungen könnten Sie haben): alles , was Sie über die Pflege werden sollen , ist die öffentliche Schnittstelle. Sie können beispielsweise drei Tests durchführen:count == 0
.isEmpty == true
Dies sind alles gültige Tests und werden besonders wichtig, wenn Sie sich jemals dazu entschließen, die Interna Ihrer Klasse umzugestalten,
isEmpty
damit eine andere Implementierung verwendet wird, die sich nicht darauf stützt.count
Solange Ihre Tests alle bestehen, wissen Sie, dass Sie nicht zurückgegangen sind etwas.Ähnliches gilt für Ihre anderen Punkte - denken Sie daran, die öffentliche Schnittstelle und nicht Ihre interne Implementierung zu testen. Sie können TDD hier nützlich finden, da Sie dann die Tests schreiben, die Sie benötigen,
isEmpty
bevor Sie überhaupt eine Implementierung dafür geschrieben haben.quelle
Sie überdenken, was ein "Komponententest" ist.
Ein Objekt, das veränderbare Daten im Speicher verwaltet, ist im Grunde eine Zustandsmaschine. Jeder wertvolle Anwendungsfall ruft also mindestens eine Methode zum Einfügen von Informationen in das Objekt und eine Methode zum Auslesen einer Kopie von Informationen aus dem Objekt auf. In den interessanten Anwendungsfällen werden Sie auch zusätzliche Methoden aufrufen, die die Datenstruktur ändern.
In der Praxis sieht das oft so aus
oder
Die "Unit-Test" -Terminologie - nun, es hat eine lange Geschichte, nicht sehr gut zu sein.
Kent schrieb die erste Version von SUnit 1994 , die Portierung auf JUnit erfolgte 1998, der erste Entwurf des TDD-Buches war Anfang 2002. Die Verwirrung hatte viel Zeit, sich auszubreiten.
Die Schlüsselidee dieser Tests (genauer gesagt "Programmierertests" oder "Entwicklertests") ist, dass die Tests voneinander isoliert sind. Die Tests haben keine veränderlichen Datenstrukturen gemeinsam, sodass sie gleichzeitig ausgeführt werden können. Es ist nicht zu befürchten, dass die Tests in einer bestimmten Reihenfolge ausgeführt werden müssen, um die Lösung korrekt zu messen.
Der primäre Anwendungsfall für diese Tests besteht darin, dass sie von der Programmiererin zwischen den Änderungen an ihrem eigenen Quellcode ausgeführt werden. Wenn Sie das Rot-Grün-Refaktor-Protokoll ausführen, zeigt ein unerwartetes ROT immer einen Fehler in Ihrer letzten Bearbeitung an. Sie machen diese Änderung rückgängig, stellen sicher, dass die Tests GRÜN sind, und versuchen es erneut. Es ist nicht sehr vorteilhaft, in ein Design zu investieren, bei dem jeder mögliche Fehler von nur einem Test erfasst wird.
Wenn eine Zusammenführung einen Fehler verursacht, ist es natürlich nicht mehr trivial, diesen Fehler zu finden. Sie können verschiedene Schritte ausführen, um sicherzustellen, dass Fehler leicht lokalisiert werden können. Sehen
quelle
Im Allgemeinen (auch wenn Sie TDD nicht verwenden) sollten Sie sich bemühen, so viele Tests wie möglich zu schreiben, während Sie so tun, als wüssten Sie nicht, wie es implementiert wird.
Wenn Sie TDD tatsächlich durchführen, sollte dies bereits der Fall sein. Ihre Tests sind eine ausführbare Spezifikation des Programms.
Wie das Aufrufdiagramm unter den Tests aussieht, ist unerheblich, solange die Tests selbst sinnvoll und gut gepflegt sind.
Ich denke, Ihr Problem ist Ihr Verständnis von TDD.
Meiner Meinung nach besteht Ihr Problem darin, dass Sie Ihre TDD-Personas "mischen". Ihre "Test" -, "Code" - und "Refactor" -Personen arbeiten im Idealfall völlig unabhängig voneinander. Insbesondere sind Ihre Kodierungs- und Umgestaltungspersonen nicht verpflichtet, die Tests anders als grün zu machen / am Laufen zu halten.
Sicher, im Prinzip wäre es am besten, wenn alle Tests orthogonal und unabhängig voneinander wären. Aber das ist kein Anliegen Ihrer beiden anderen TDD-Persönlichkeiten, und es ist definitiv keine strenge oder sogar unbedingt realistische Anforderung an Ihre Tests. Grundsätzlich gilt: Wirf nicht deinen gesunden Menschenverstand in Bezug auf die Codequalität aus, um zu versuchen, eine Anforderung zu erfüllen, die niemand von dir verlangt.
quelle