Ich überarbeite derzeit einen Teil einer großen Codebasis ohne Unit-Tests. Ich habe versucht, Code auf brutale Weise umzugestalten, dh indem ich versucht habe zu erraten, was der Code tut und welche Änderungen seine Bedeutung nicht ändern würden, aber ohne Erfolg: Es werden zufällig Funktionen rund um die Codebasis unterbrochen.
Beachten Sie, dass das Refactoring das Verschieben von Legacy-C # -Code in einen funktionaleren Stil umfasst (der Legacy-Code verwendet keine der Funktionen von .NET Framework 3 und höher, einschließlich LINQ), das Hinzufügen von Generika, bei denen der Code davon profitieren kann usw.
Ich kann keine formalen Methoden anwenden, wenn man bedenkt, wie viel sie kosten würden.
Andererseits gehe ich davon aus, dass mindestens die Regel "Jeder überarbeitete Legacy-Code muss mit Komponententests geliefert werden" strikt befolgt werden sollte, unabhängig davon, wie viel es kosten würde. Das Problem ist, dass das Hinzufügen von Komponententests eine schwierige Aufgabe ist, wenn ich einen winzigen Teil einer privaten 500-LOC-Methode umgestalte.
Was kann mir helfen zu wissen, welche Komponententests für einen bestimmten Code relevant sind? Ich vermute, dass eine statische Analyse des Codes irgendwie hilfreich wäre, aber mit welchen Tools und Techniken kann ich Folgendes tun:
Wissen Sie genau, welche Unit-Tests ich erstellen soll,
Und / oder wissen Sie, ob die von mir vorgenommene Änderung den ursprünglichen Code so beeinflusst hat, dass er anders ausgeführt wird als jetzt?
quelle
formal methods in software development
sowieso nicht verwenden wollen, da es verwendet wird, um die Richtigkeit eines Programms mithilfe von Prädikatenlogik zu beweisen, und nicht für die Umgestaltung einer großen Codebasis anwendbar wäre. Formale Methoden, die normalerweise zum Nachweis von Code verwendet werden, funktionieren in Bereichen wie medizinischen Anwendungen korrekt. Sie haben Recht, es ist teuer zu tun, weshalb es nicht oft verwendet wird.Antworten:
Ich hatte ähnliche Herausforderungen. Das Buch Arbeiten mit Legacy-Code ist eine großartige Ressource, aber es wird davon ausgegangen, dass Sie in Unit-Tests Schuhhupen verwenden können, um Ihre Arbeit zu unterstützen. Manchmal ist das einfach nicht möglich.
In meiner archäologischen Arbeit (mein Begriff für die Wartung von Legacy-Code wie diesem) verfolge ich einen ähnlichen Ansatz wie den von Ihnen beschriebenen.
Zu diesem Zeitpunkt sollten Sie eine Kandidatenliste darüber haben, was von dieser Routine freigelegt und / oder manipuliert wurde. Einige dieser Manipulationen sind wahrscheinlich unbeabsichtigt. Jetzt benutze ich
findstr
und die IDE, um zu verstehen, welche anderen Bereiche auf die Elemente in der Kandidatenliste verweisen könnten. Ich werde einige Zeit damit verbringen zu verstehen, wie diese Referenzen funktionieren und wie sie beschaffen sind.Sobald ich mich getäuscht habe zu glauben, die Auswirkungen der ursprünglichen Routine zu verstehen, werde ich meine Änderungen einzeln vornehmen und die oben beschriebenen Analyseschritte erneut ausführen, um zu überprüfen, ob die Änderung wie erwartet funktioniert es zu arbeiten. Ich versuche ausdrücklich zu vermeiden, dass mehrere Dinge gleichzeitig geändert werden, da ich festgestellt habe, dass dies mich in die Luft jagt, wenn ich versuche, die Auswirkungen zu überprüfen. Manchmal kann man mit mehreren Änderungen davonkommen, aber wenn ich einer einzelnen Route folgen kann, ist das meine Präferenz.
Kurz gesagt, mein Ansatz ähnelt dem, was Sie dargelegt haben. Es ist viel Vorarbeit; dann nehmen Sie umsichtige, individuelle Änderungen vor; und dann überprüfen, überprüfen, überprüfen.
quelle
Kurze Antwort: kleine Schritte.
Betrachten Sie diese Schritte:
Verschieben Sie die Implementierung in eine andere (private) Funktion und delegieren Sie den Aufruf.
Fügen Sie in Ihrer ursprünglichen Funktion für alle Ein- und Ausgänge Protokollierungscode hinzu (stellen Sie sicher, dass die Protokollierung nicht fehlschlägt).
Führen Sie Ihre Anwendung aus und tun Sie alles, was Sie können (gültige Verwendung, ungültige Verwendung, typische Verwendung, atypische Verwendung usw.).
Sie haben jetzt eine
max(call_count)
Reihe von Ein- und Ausgängen, mit denen Sie Ihre Tests schreiben können. Sie können einen einzelnen Test schreiben , der alle Ihre vorhandenen Parameter / Ergebnismengen durchläuft und diese in einer Schleife ausführt. Sie können auch einen zusätzlichen Test schreiben, der eine bestimmte Kombination ausführt (um schnell zu überprüfen, ob ein bestimmter E / A-Satz übergeben wird).Kehren Sie
// 500 LOC here
zu Ihrerugly500loc
Funktion zurück (und entfernen Sie die Protokollierungsfunktion).Beginnen Sie mit dem Extrahieren von Funktionen aus der großen Funktion (tun Sie nichts anderes, extrahieren Sie nur Funktionen) und führen Sie Tests aus. Danach sollten Sie mehr kleine Funktionen zum Refactor haben als die 500LOC.
Lebe glücklich bis ans Ende.
quelle
Normalerweise sind Unit Tests der richtige Weg.
Führen Sie die erforderlichen Tests durch, um zu beweisen, dass der Strom wie erwartet funktioniert. Nehmen Sie sich Zeit und der letzte Test muss Sie von der Ausgabe überzeugen.
Sie sind dabei, einen Code zu überarbeiten. Sie müssen genau wissen, was er tut und welche Auswirkungen er hat. Grundsätzlich müssen Sie also alle betroffenen Zonen testen. Dies wird viel Zeit in Anspruch nehmen ... aber das ist das erwartete Ergebnis eines jeden Refactoring-Prozesses.
Dann können Sie alles problemlos auseinander reißen.
AFAIK, dafür gibt es keine kugelsichere Technik ... Sie müssen nur methodisch sein (bei welcher Methode auch immer Sie sich wohl fühlen), viel Zeit und viel Geduld! :) :)
Prost und viel Glück!
Alex
quelle