Wir haben fast 3.000 Tests geschrieben - die Daten wurden fest codiert, die Wiederverwendung von Code ist sehr gering. Diese Methode hat begonnen, uns in den Arsch zu beißen. Wenn sich das System ändert, verbringen wir mehr Zeit damit, kaputte Tests zu reparieren. Wir haben Unit-, Integrations- und Funktionstests.
Was ich suche, ist eine definitive Möglichkeit, verwaltbare und wartbare Tests zu schreiben.
Frameworks
.net
unit-testing
Chuck Conway
quelle
quelle
Antworten:
Betrachten Sie sie nicht als "defekte Komponententests", da dies nicht der Fall ist.
Dies sind Spezifikationen, die Ihr Programm nicht mehr unterstützt.
Betrachten Sie es nicht als "Fixieren der Tests", sondern als "Definieren neuer Anforderungen".
Bei den Tests sollte zunächst Ihre Anwendung angegeben werden, nicht umgekehrt.
Sie können nicht sagen, dass Sie eine funktionierende Implementierung haben, bis Sie wissen, dass sie funktioniert. Sie können nicht sagen, dass es funktioniert, bis Sie es testen.
Ein paar andere Hinweise, die Sie leiten könnten:
quelle
Don't think of it as "fixing the tests", but as "defining new requirements".
Was Sie beschreiben, ist vielleicht gar nicht so schlecht, aber ein Hinweis auf tiefere Probleme, die Ihre Tests entdecken
Wenn Sie Ihren Code ändern könnten und Ihre Tests nicht abbrechen würden, wäre das für mich verdächtig. Der Unterschied zwischen einer legitimen Änderung und einem Fehler besteht nur in der Tatsache, dass sie angefordert wird. Was angefordert wird, wird von Ihren Tests definiert (vorausgesetzt TDD).
Fest codierte Daten in Tests sind imho eine gute Sache. Tests funktionieren als Fälschungen, nicht als Beweise. Wenn zu viel berechnet wird, sind Ihre Tests möglicherweise Tautologien. Beispielsweise:
Je höher die Abstraktion, desto näher kommt man dem Algorithmus und damit dem Vergleich der tatsächlichen Implementierung mit sich selbst.
Die beste Wiederverwendung von Code in Tests ist imho 'Checks', wie in jUnits
assertThat
, weil sie die Tests einfach halten. Wenn die Tests so umgestaltet werden können, dass sie Code gemeinsam nutzen, ist dies wahrscheinlich auch der tatsächlich getestete Code , wodurch die Tests auf diejenigen reduziert werden, die die umgestaltete Basis testen.quelle
Ich hatte auch dieses Problem. Mein verbesserter Ansatz war wie folgt:
Schreiben Sie keine Komponententests, es sei denn, dies ist der einzig gute Weg, um etwas zu testen.
Ich bin voll und ganz bereit zuzugeben, dass Unit-Tests die niedrigsten Kosten für Diagnose und Reparatur aufweisen. Das macht sie zu einem wertvollen Werkzeug. Das Problem ist, dass Unit-Tests angesichts der offensichtlichen Abweichungen Ihrer Laufleistung oft zu kleinlich sind, um die Kosten für die Aufrechterhaltung der Codemasse zu verdienen. Ich habe unten ein Beispiel geschrieben, schauen Sie es sich an.
Verwenden Sie Aussagen, wo immer sie dem Komponententest für diese Komponente entsprechen. Assertions haben die nette Eigenschaft, dass sie bei jedem Debugbuild immer überprüft werden. Anstatt die Klasseneinschränkungen für "Mitarbeiter" in einer separaten Testeinheit zu testen, testen Sie die Klasse "Mitarbeiter" effektiv für jeden Testfall im System. Behauptungen haben auch die nette Eigenschaft, dass sie die Codemasse nicht so stark erhöhen wie Unit-Tests (die schließlich ein Gerüst / Verspotten / was auch immer erfordern).
Bevor mich jemand umbringt: Produktions-Builds sollten nicht aufgrund von Behauptungen abstürzen. Sie sollten stattdessen auf der Ebene "Fehler" protokollieren.
Machen Sie als Warnung für jemanden, der noch nicht darüber nachgedacht hat, keine Angaben zu Benutzer- oder Netzwerkeingaben. Es ist ein großer Fehler ™.
In meinen neuesten Codebasen habe ich Unit-Tests mit Bedacht entfernt, wo immer ich eine offensichtliche Möglichkeit für Behauptungen sehe. Dies hat die Wartungskosten insgesamt erheblich gesenkt und mich zu einer viel glücklicheren Person gemacht.
Bevorzugen Sie System- / Integrationstests und implementieren Sie diese für alle Ihre primären Abläufe und Benutzererfahrungen. Eckkoffer müssen wohl nicht dabei sein. Ein Systemtest überprüft das Verhalten auf Benutzerseite, indem alle Komponenten ausgeführt werden. Aus diesem Grund ist ein Systemtest notwendigerweise langsamer. Schreiben Sie daher die wichtigsten auf (nicht mehr und nicht weniger), und Sie werden die wichtigsten Probleme erkennen. Systemtests haben einen sehr geringen Wartungsaufwand.
Da Sie Behauptungen verwenden, werden bei jedem Systemtest gleichzeitig ein paar hundert "Komponententests" ausgeführt. Sie können sich auch ziemlich sicher sein, dass die wichtigsten mehrmals ausgeführt werden.
Schreiben Sie starke APIs, die funktional getestet werden können. Funktionstests sind umständlich und (seien wir ehrlich) bedeutungslos, wenn Ihre API es zu schwierig macht, funktionierende Komponenten selbst zu überprüfen. Ein gutes API-Design a) macht die Testschritte einfach und b) erzeugt klare und wertvolle Aussagen.
Funktionstests sind am schwierigsten durchzuführen, insbesondere wenn Komponenten über Prozessgrenzen hinweg miteinander kommunizieren. Je mehr Ein- und Ausgänge an eine einzelne Komponente angeschlossen sind, desto schwieriger gestaltet sich die Funktionsprüfung, da Sie einen davon isolieren müssen, um seine Funktionalität wirklich zu testen.
Zum Thema "Komponententests nicht schreiben" stelle ich ein Beispiel vor:
Der Autor dieses Tests hat sieben Zeilen hinzugefügt, die nicht dazu beitragen , überhaupt auf die Überprüfung des Endprodukts. Der Benutzer sollte dies niemals sehen, entweder weil a) niemand dort NULL übergeben sollte (schreiben Sie dann eine Behauptung) oder b) der NULL-Fall ein anderes Verhalten hervorrufen sollte. Wenn der Fall (b) ist, schreiben Sie einen Test, der dieses Verhalten tatsächlich überprüft.
Meine Philosophie ist, dass wir keine Implementierungsartefakte testen sollten. Wir sollten nur alles testen, was als tatsächliche Ausgabe betrachtet werden kann. Andernfalls kann nicht vermieden werden, dass zwischen den Komponententests (die eine bestimmte Implementierung erzwingen) und der Implementierung selbst die doppelte Menge an Code geschrieben wird.
Hierbei ist zu beachten, dass es gute Kandidaten für Unit-Tests gibt. In der Tat gibt es sogar mehrere Situationen, in denen ein Komponententest das einzig angemessene Mittel ist, um etwas zu verifizieren, und in denen es von hohem Wert ist, diese Tests zu schreiben und aufrechtzuerhalten. Von Anfang an enthält diese Liste nicht-triviale Algorithmen, offen gelegte Datencontainer in einer API und hochoptimierten Code, der "kompliziert" erscheint (auch bekannt als "der Nächste wird es wahrscheinlich vermasseln").
Mein spezifischer Rat an Sie: Starten Sie das Löschen von Unit-Tests mit Bedacht, wenn sie brechen, und stellen Sie sich die Frage: "Ist dies eine Ausgabe oder verschwende ich Code?" Es wird Ihnen wahrscheinlich gelingen, die Anzahl der Dinge zu reduzieren, die Ihre Zeit verschwenden.
quelle
Scheint mir, als ob Ihre Unit-Tests wie ein Zauber wirken. Es ist eine gute Sache, dass es so anfällig für Veränderungen ist, denn das ist der springende Punkt. Kleine Änderungen bei Code-Break-Tests, damit Sie die Möglichkeit von Fehlern in Ihrem gesamten Programm beseitigen können.
Denken Sie jedoch daran, dass Sie nur wirklich auf Bedingungen testen müssen, die dazu führen, dass Ihre Methode fehlschlägt oder unerwartete Ergebnisse liefert. Dies würde dazu führen, dass Ihre Einheitstests anfälliger für "Brüche" sind, wenn es eher ein echtes Problem als triviale Dinge gibt.
Allerdings scheint es mir, dass Sie das Programm stark umgestalten. Führen Sie in solchen Fällen alle erforderlichen Schritte aus, entfernen Sie die alten Tests und ersetzen Sie sie anschließend durch neue. Das Reparieren von Unit-Tests lohnt sich nur, wenn Sie sie aufgrund grundlegender Änderungen in Ihrem Programm nicht reparieren. Andernfalls wird möglicherweise zu viel Zeit für das Umschreiben von Tests aufgewendet, um in Ihrem neu geschriebenen Abschnitt des Programmcodes angewendet zu werden.
quelle
Ich bin sicher, dass andere viel mehr Input haben werden, aber meiner Erfahrung nach sind dies einige wichtige Dinge, die Ihnen helfen werden:
quelle
Behandeln Sie Tests so, wie Sie es mit Quellcode tun.
Versionskontrolle, Checkpoint-Releases, Issue-Tracking, "Feature Ownership", Planung und Aufwandsschätzung usw. Ich denke, dies ist der effizienteste Weg, um mit Problemen umzugehen, die Sie beschreiben.
quelle
Schauen Sie sich unbedingt die XUnit-Testmuster von Gerard Meszaros an . Es gibt einen großartigen Abschnitt mit vielen Rezepten, mit denen Sie Ihren Testcode wiederverwenden und Doppelungen vermeiden können.
Wenn Ihre Tests spröde sind, kann es auch sein, dass Sie nicht genug zurückgreifen, um Doppel zu testen. Insbesondere wenn Sie zu Beginn jedes Komponententests ganze Diagramme von Objekten neu erstellen, werden die Arrangierabschnitte in Ihren Tests möglicherweise zu groß und Sie befinden sich häufig in Situationen, in denen Sie die Arrangierabschnitte in einer beträchtlichen Anzahl von Tests neu schreiben müssen, nur weil Eine Ihrer am häufigsten verwendeten Klassen hat sich geändert. Mocks und Stubs können Ihnen dabei helfen, indem sie die Anzahl der Objekte reduzieren, die Sie rehydrieren müssen, um einen relevanten Testkontext zu erhalten.
Wenn Sie die unwichtigen Details aus Ihren Testaufbauten über Mocks und Stubs entfernen und Testmuster anwenden, um Code wiederzuverwenden, sollte dies deren Fragilität erheblich verringern.
quelle