Schreiben von Tests für vorhandenen Code


68

Angenommen, man hat ein relativ großes Programm (etwa 900.000 SLOC in C #), das alle gründlich kommentiert / dokumentiert ist, gut organisiert ist und gut funktioniert. Die gesamte Codebasis wurde von einem einzigen Senior-Entwickler geschrieben, der nicht mehr im Unternehmen ist. Der gesamte Code ist so wie er ist testbar und IoC wird durchgehend verwendet - außer aus irgendeinem seltsamen Grund haben sie keine Komponententests geschrieben. Jetzt möchte Ihr Unternehmen den Code verzweigen und Unit-Tests hinzufügen, um festzustellen, wenn Änderungen die Kernfunktionalität beeinträchtigen.

  • Ist das Hinzufügen von Tests eine gute Idee?
  • Wenn ja, wie würde man überhaupt mit so etwas anfangen?

BEARBEITEN

OK, also hatte ich nicht damit gerechnet, dass Antworten gute Argumente für gegensätzliche Schlussfolgerungen liefern. Das Problem kann sowieso aus meinen Händen sein. Ich habe auch die "doppelten Fragen" durchgelesen und die allgemeine Meinung ist, dass "das Schreiben von Tests gut ist" ... ja, aber in diesem speziellen Fall nicht zu hilfreich.

Ich glaube nicht, dass ich hier allein bin, um über das Schreiben von Tests für ein Altsystem nachzudenken. Ich werde Metriken darüber erstellen, wie viel Zeit aufgewendet wird und wie oft die neuen Tests Probleme aufdecken (und wie oft nicht). Ich werde wiederkommen und dies in etwa einem Jahr mit meinen Ergebnissen aktualisieren.

FAZIT

Es stellt sich also heraus, dass es im Grunde unmöglich ist, einen Unit-Test mit irgendeinem orthodoxen Anschein zu einem bestehenden Code hinzuzufügen. Sobald der Code funktioniert, können Sie Ihre Tests offensichtlich nicht rot / grün leuchten lassen. In der Regel ist nicht klar, welche Verhaltensweisen zu testen sind, wo Sie beginnen sollen und erst recht nicht, wann Sie fertig sind. Wirklich selbst diese Frage zu stellen, verfehlt in erster Linie den Hauptgrund, Tests zu schreiben. In den meisten Fällen fand ich es tatsächlich einfacher, den Code mit TDD neu zu schreiben, als die beabsichtigten Funktionen zu entschlüsseln und nachträglich in Komponententests hinzuzufügen. Wenn Sie ein Problem beheben oder eine neue Funktion hinzufügen, ist dies eine andere Geschichte, und ich glaube, dass dies der Zeitpunkt ist, um Komponententests hinzuzufügen (wie einige unten dargelegt haben). Irgendwann wird der meiste Code umgeschrieben, oft früher als erwartet.


10
Durch das Hinzufügen von Tests wird der vorhandene Code nicht beschädigt.
Dan Pichelman

30
@DanPichelman Sie haben noch nie einen Schroedinbug erlebt - "Ein Entwurfs- oder Implementierungsfehler in einem Programm, der erst auftritt, wenn jemand eine Quelle liest oder das Programm auf ungewöhnliche Weise verwendet, stellt fest, dass es niemals hätte funktionieren sollen. An diesem Punkt wird das Programm umgehend gestartet hört auf, für alle zu arbeiten, bis es behoben ist. "

8
@MichaelT Nun, da Sie es erwähnen, glaube ich, dass ich ein oder zwei davon gesehen habe. Mein Kommentar sollte lauten: "Das Hinzufügen von Tests führt normalerweise nicht dazu, dass vorhandener Code beschädigt wird." Vielen Dank
Dan Pichelman

3
Schreiben Sie nur Tests zu Dingen, die Sie überarbeiten oder ändern möchten.
Steven Evers

3
Lesen Sie das Buch "Effektiv mit Legacy-Code arbeiten": amazon.com/Working-Effective-Legacy-Michael-Feathers/dp/… . Michael Feathers empfiehlt, die Tests zu schreiben, bevor Sie Legacy-Code ändern .
Skarab

Antworten:


68

Während Tests eine gute Idee sind, sollte der ursprüngliche Codierer sie erstellen, während er die Anwendung erstellte , um sein Wissen darüber zu erfassen, wie der Code funktionieren soll und was möglicherweise kaputt geht, was dann an Sie übertragen worden wäre.

Bei diesem Ansatz besteht eine hohe Wahrscheinlichkeit, dass Sie die Tests schreiben, die mit der geringsten Wahrscheinlichkeit brechen, und die meisten Randfälle übersehen, die beim Erstellen der Anwendung entdeckt worden wären.

Das Problem ist, dass der größte Teil des Werts aus diesen „Fallstricken“ und weniger offensichtlichen Situationen stammt. Ohne diese Tests verliert die Testsuite praktisch ihre gesamte Wirksamkeit. Darüber hinaus hat das Unternehmen ein falsches Sicherheitsgefühl in Bezug auf seine Anwendung, da diese nicht wesentlich regressionssicherer ist.

In der Regel müssen Tests für neuen Code und für das Refactoring alten Codes geschrieben werden, bis die alte Codebasis vollständig refactored ist.

Auch sehen .


11
Aber auch ohne TDD sind Unit-Tests beim Refactoring nützlich.
pdr

1
Wenn der Code weiterhin einwandfrei funktioniert, ist dies kein Problem. Am besten testen Sie jedoch die Schnittstelle zum Legacy-Code, wenn Sie etwas schreiben, das von dessen Verhalten abhängt.
Deworde

1
Aus diesem Grund messen Sie die Testabdeckung . Wenn der Test für einen bestimmten Abschnitt nicht alle Wenn und Elsen und alle Randfälle abdeckt, können Sie diesen Abschnitt nicht sicher umgestalten. Die Abdeckung zeigt an, ob alle Linien getroffen wurden. Ihr Ziel ist es daher, die Abdeckung vor dem Refactoring so weit wie möglich zu erhöhen.
Rudolf Olah

3
Ein wesentlicher Nachteil von TDD ist, dass die Suite zwar ausgeführt werden kann, für Entwickler, die mit der Codebasis nicht vertraut sind, dies jedoch ein falsches Sicherheitsgefühl darstellt. BDD ist in dieser Hinsicht viel besser, da die Ausgabe die Absicht des Codes in einfachem Englisch ist.
Robbie Dee

3
Nur um zu erwähnen, dass 100% Code-Abdeckung nicht bedeutet, dass Ihr Code 100% der Zeit korrekt funktioniert. Sie können jede Codezeile für die Methode testen lassen, aber nur weil sie mit value1 funktioniert, heißt das nicht, dass die Funktion mit value2 garantiert ist.
Ryanzec

35

Ja, das Hinzufügen von Tests ist definitiv eine gute Idee.

Sie sagen, es ist gut dokumentiert, und das bringt Sie in eine gute Position. Versuchen Sie, anhand dieser Dokumentation Tests zu erstellen, die sich auf Teile des Systems konzentrieren, die entweder kritisch sind oder häufigen Änderungen unterliegen.

Anfangs wird die schiere Größe der Codebasis im Vergleich zu den wenigen Tests wahrscheinlich überwältigend erscheinen, aber es gibt keinen Big-Bang-Ansatz, und es ist wichtiger, irgendwo einen Anfang zu machen, als sich zu quälen, welcher Ansatz der beste sein wird.

Ich würde Michael Feathers 'Buch Working Effectively with Legacy Code für einige gute, detaillierte Ratschläge empfehlen .


10
+1. Wenn Sie die Tests auf der Grundlage der Dokumentation schreiben, stellen Sie fest, dass zwischen dem Arbeitscode und der Dokumentation Unstimmigkeiten bestehen, die von unschätzbarem Wert sind.
Carl Manaster

1
Ein weiterer Grund für das Hinzufügen von Tests: Wenn ein Fehler gefunden wird, können Sie problemlos einen Testfall für zukünftige Regressionstests hinzufügen.

Diese Strategie wird im Wesentlichen im (kostenlosen) edX-Onlinekurs CS169.2x im Kapitel über Legacy-Code beschrieben. Wie die Lehrer es sagen: " Feststellung
FGM

21

Nicht alle Komponententests haben den gleichen Nutzen. Der Vorteil eines Komponententests besteht darin, dass er fehlschlägt. Je unwahrscheinlicher das Scheitern ist, desto weniger vorteilhaft ist es. Neuer oder kürzlich geänderter Code enthält mit größerer Wahrscheinlichkeit Fehler als selten geänderter Code, der in der Produktion gut getestet wurde. Daher sind Unit-Tests für neuen oder kürzlich geänderten Code mit größerer Wahrscheinlichkeit vorteilhafter.

Nicht alle Komponententests sind gleich teuer. Es ist viel einfacher, Trivialcode zu testen, den Sie heute selbst entworfen haben, als komplexen Code, den jemand anderes vor langer Zeit entworfen hat. Auch das Testen während der Entwicklung spart normalerweise Entwicklungszeit. Bei älteren Codes ist diese Kostenersparnis nicht mehr verfügbar.

In einer idealen Welt haben Sie die Zeit, die Sie zum Testen von Legacy-Code in Einheiten benötigen. In der Realität ist es jedoch naheliegend, dass die Kosten für das Hinzufügen von Unit-Tests zu Legacy-Code die Vorteile überwiegen. Der Trick besteht darin, diesen Punkt zu identifizieren. Ihre Versionskontrolle kann Ihnen helfen, indem Sie den zuletzt geänderten und am häufigsten geänderten Code anzeigen. Sie können beginnen, indem Sie diese unter Unit-Test stellen. Wenn Sie in Zukunft Änderungen vornehmen, sollten Sie diese Änderungen und den eng damit verbundenen Code einem Komponententest unterziehen.

Wenn Sie dieser Methode folgen, werden Sie in den günstigsten Bereichen eine ziemlich gute Abdeckung haben. Wenn Sie stattdessen Monate damit verbringen, Komponententests durchzuführen, bevor Sie die umsatzgenerierenden Aktivitäten wieder aufnehmen, ist dies möglicherweise eine wünschenswerte Entscheidung für die Softwarewartung, aber eine miese Geschäftsentscheidung.


3
Wenn der Code selten fehlschlägt, kann viel Zeit und Mühe aufgewendet werden, um arkane Probleme zu finden, die im wirklichen Leben niemals auftreten könnten. Wenn der Code fehlerhaft und fehleranfällig ist, können Sie wahrscheinlich überall mit dem Testen beginnen und Probleme sofort feststellen.
Robbie Dee

8

Ist das Hinzufügen von Tests eine gute Idee?

Auf jeden Fall, obwohl ich es ein bisschen schwer finde zu glauben, dass der Code sauber ist, gut funktioniert und moderne Techniken verwendet und einfach keine Komponententests hat. Sind Sie sicher, dass sie nicht in einer separaten Lösung sitzen?

Egal, wenn Sie den Code erweitern / warten möchten, sind echte Komponententests für diesen Prozess von unschätzbarem Wert.

Wenn ja, wie würde man überhaupt mit so etwas anfangen?

Ein Schritt auf einmal. Wenn Sie mit Unit-Tests nicht vertraut sind, lernen Sie ein wenig. Sobald Sie mit den Konzepten vertraut sind, wählen Sie einen kleinen Teil des Codes aus und schreiben Sie Tests dafür. Dann das nächste und das nächste. Die Codeabdeckung kann Ihnen dabei helfen, Stellen zu finden, die Sie verpasst haben.

Es ist wahrscheinlich am besten, zuerst gefährliche / riskante / lebenswichtige Dinge auszuwählen, um sie zu testen, aber möglicherweise ist es effektiver, wenn Sie etwas Unkompliziertes testen, um zuerst in einen Groove zu gelangen - insbesondere, wenn Sie / das Team nicht an die Codebasis und / oder die Einheit gewöhnt sind testen.


7
"Sind Sie sicher, dass sie nicht in einer separaten Lösung sitzen?" ist eine gute Frage. Ich hoffe, das OP übersieht es nicht.
Dan Pichelman

Leider gab es keine Chance, dass die Anwendung vor vielen Jahren gestartet wurde, als TDD an Fahrt gewann. Daher war die Absicht, irgendwann Tests durchzuführen, aber aus irgendeinem Grund kamen sie zu Beginn des Projekts nie dazu.
Paul

2
Sie sind wahrscheinlich nie dazu gekommen, weil es ihnen zusätzliche Zeit gekostet hätte, die es nicht wert war. Ein guter Entwickler, der alleine arbeitet, kann definitiv eine saubere, übersichtliche, gut organisierte und funktionierende Anwendung von relativ großer Größe ohne automatisierte Tests erstellen, und im Allgemeinen kann er dies schneller und genauso fehlerfrei wie bei Tests. Da alles in ihrem eigenen Kopf liegt, ist die Wahrscheinlichkeit von Fehlern oder organisatorischen Problemen erheblich geringer als bei der Erstellung durch mehrere Entwickler.
Ben Lee

3

Ja, Tests sind eine gute Idee. Sie helfen dabei, die vorhandenen Codebasis-Funktionen wie beabsichtigt zu dokumentieren und unerwartetes Verhalten zu erkennen. Selbst wenn die Tests anfangs fehlschlagen, lassen Sie sie den Code später überarbeiten, damit sie bestanden werden und sich wie beabsichtigt verhalten.

Beginnen Sie mit dem Schreiben von Tests für kleinere Klassen (solche, die keine Abhängigkeiten aufweisen und relativ einfach sind) und fahren Sie mit größeren Klassen fort (solche, die Abhängigkeiten aufweisen und komplexer sind). Es wird lange dauern, aber seien Sie geduldig und beharrlich, damit Sie die Codebasis schließlich so gut wie möglich abdecken können.


Würden Sie einem gut funktionierenden Programm wirklich fehlgeschlagene Tests hinzufügen (so das OP)?
MarkJ

Ja, weil es zeigt, dass etwas nicht wie beabsichtigt funktioniert und einer weiteren Überprüfung bedarf. Dies wird zu Diskussionen führen und hoffentlich Missverständnisse oder zuvor unbekannte Mängel beseitigen.
Bernard

@Bernard - oder, die Tests können Ihr Missverständnis darüber aufdecken, was der Code tun soll. Später geschriebene Tests laufen Gefahr, die ursprünglichen Absichten nicht korrekt wiederzugeben.
Dan Pichelman

@DanPichelman: Einverstanden, aber das sollte einen nicht davon abhalten, überhaupt irgendwelche Tests zu schreiben.
Bernard

Wenn nichts anderes angezeigt wird, wurde der Code nicht defensiv geschrieben.
Robbie Dee

3

OK, ich werde die gegenteilige Meinung vertreten ...

Das Hinzufügen von Tests zu einem vorhandenen, funktionierenden System wird dieses System ändern, es sei denn, das System wurde von Anfang an mit spöttischer Absicht geschrieben. Ich bezweifle es, obwohl es durchaus möglich ist, dass es eine gute Trennung aller Komponenten mit leicht definierbaren Grenzen gibt, in die Sie Ihre nachgebildeten Schnittstellen einfügen können. Wenn dies nicht der Fall ist, müssen Sie (relativ gesehen) wichtige Änderungen vornehmen, die möglicherweise zu Problemen führen. Im besten Fall verbringen Sie viel Zeit damit, diese Tests zu erstellen. Diese Zeit könnte besser verwendet werden, um ausführliche Konstruktionsdokumente, Auswirkungsanalysen oder Lösungskonfigurationsdokumente zu erstellen. Immerhin ist das die Arbeit, die Ihr Chef mehr als Unit-Tests machen möchte. Ist es nicht

Wie auch immer, ich würde keine Unit-Tests hinzufügen.

Ich würde mich auf externe, automatisierte Testtools konzentrieren, die Ihnen eine angemessene Abdeckung bieten, ohne etwas zu ändern. Wenn Sie dann Änderungen vornehmen möchten, können Sie damit beginnen, Komponententests in die Codebasis aufzunehmen.


2

Ich bin oft auf diese Situation gestoßen, habe eine große Codebasis ohne ausreichende oder keine Testabdeckung geerbt und bin jetzt für das Hinzufügen von Funktionen, das Beheben von Fehlern usw. verantwortlich.

Mein Rat ist, sicherzustellen und zu testen, was Sie hinzufügen. Wenn Sie Fehler beheben oder Anwendungsfälle im aktuellen Code ändern, testet der Autor diese. Wenn Sie etwas anfassen müssen, schreiben Sie an dieser Stelle Tests.

Dies ist der Fall, wenn der vorhandene Code für Komponententests nicht gut strukturiert ist. Sie verbringen daher viel Zeit mit der Umgestaltung, um Tests für kleinere Änderungen hinzuzufügen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.