2009-02-06 11 views
11

Nachdem ich gerade die ersten vier Kapitel von Refactoring: Improving the Design of Existing Code gelesen hatte, begann ich mit meinem ersten Refactoring und kam fast sofort zu einer Straßensperre. Es beruht auf der Anforderung, dass Sie vor dem Refactoring Unit-Tests um den Legacy-Code herum platzieren sollten. Dadurch können Sie sicher sein, dass Ihr Refactoring nicht geändert hat, was der ursprüngliche Code getan hat (nur wie es tat es).Praktisches Refactoring mit Unit Tests

Meine erste Frage lautet also: Wie teste ich eine Methode im Legacy-Code? Wie kann ich einen Komponententest um eine 500-Linien-Methode (wenn ich Glück habe) setzen, die nicht nur eine Aufgabe erfüllt? Es scheint mir, dass ich meinen Legacy-Code umgestalten müsste, nur um ihn testbar zu machen.

Hat jemand irgendwelche Erfahrungen mit Refactoring Unit Tests? Und wenn ja, haben Sie praktische Beispiele, die Sie mit mir teilen können?

Meine zweite Frage ist etwas schwer zu erklären. Hier ein Beispiel: Ich möchte eine Legacy-Methode umgestalten, die ein Objekt aus einem Datenbankeintrag auffüllt. Müsste ich nicht einen Komponententest schreiben, der ein mit der alten Methode abgerufenes Objekt mit einem Objekt vergleicht, das mit meiner refaktorierten Methode abgerufen wurde? Wie würde ich sonst wissen, dass meine refaktorierte Methode die gleichen Ergebnisse wie die alte Methode liefert? Wenn das wahr ist, wie lange belasse ich dann die alte veraltete Methode im Quellcode? Schläge ich es einfach, nachdem ich ein paar verschiedene Platten getestet habe? Oder muss ich es für eine Weile behalten, falls ich einen Fehler in meinem refaktorierten Code erhalte?

Zuletzt, da ein paar Leute gefragt haben ... der Legacy-Code wurde ursprünglich in VB6 geschrieben und dann mit minimalen Änderungen der Architektur zu VB.NET portiert.

+0

Große Frage. Sie können auch versuchen, Katas, die Ihnen hilft, eine Angewohnheit zu schreiben einen guten Code und wie Sie Einheit getestet Code schreiben: https://github.com/garora/TDD-Katas –

Antwort

4

Gutes Beispiel für eine Theorie, die der Realität entspricht. Komponententests sollen eine einzelne Operation testen, und viele Musterpuristen bestehen auf Single Responsibilty, also haben wir schönen sauberen Code und Tests, um damit zu gehen. In der realen (unordentlichen) Welt macht jedoch Code (insbesondere Legacy-Code) viele Dinge und hat keine Tests. Was das braucht, ist eine Dosis Refaktorierung, um das Chaos zu beseitigen.

Mein Ansatz besteht darin, Tests zu erstellen, die Unit Test Tools verwenden, die viele Dinge in einem einzigen Test testen. In einem Test kann ich prüfen, ob die DB-Verbindung offen ist, viele Daten ändern und eine Vorher/Nachher-Überprüfung der Datenbank durchführen. Ich finde unweigerlich, dass ich Hilfsklassen schreibe, um die Überprüfung durchzuführen, und in den meisten Fällen können diese Helfer dann in die Codebasis eingefügt werden, da sie emergentes Verhalten/Logik/Anforderungen eingekapselt haben. Ich meine nicht, dass ich einen einzigen großen Test habe. Was ich meine ist, dass Tests eine Arbeit machen, die ein Purist als Integrationstest bezeichnen würde - gibt es so etwas noch? Auch habe ich es nützlich gefunden, eine Testvorlage zu erstellen und dann viele Tests daraus zu erstellen, um Randbedingungen, komplexe Verarbeitung etc. zu überprüfen.

BTW welche Sprachumgebung sprechen wir? Einige Sprachen eignen sich besser zum Refactoring als andere.

+0

Ich streite oft über Kompromisse auf der pragmatischen Seite einer solchen puristischen Diskussion. Vielleicht werde ich einfach alt und habe nicht den Kampf in mir, die puristische Ladung mehr zu führen. ;) – JMD

+0

@ JMD.Außerdem ist der einzige Komponententest, der für das Refactoring sinnvoll ist, ein End-to-End-Funktionseinheitstest mit minimaler Abhängigkeit von der internen Struktur. Zur gleichen Zeit spricht niemand darüber. Meine Vermutung ist, dass es nur sehr wenig Umgestaltung gibt: Der vorhandene Code ist so schlecht, dass in den meisten Fällen das Refactoring durch den Austausch von Funktionseinheiten erfolgt. – zzz777

0

Das ist wirklich eines der Hauptprobleme beim Versuch, Legacy-Code nachzurüsten. Sind Sie in der Lage, die Problemdomäne auf etwas Granulares zu reduzieren? Führt diese 500 + Zeilenmethode alles andere als Systemaufrufe zu JDK/Win32/.NET Framework JARs/DLLs/Assemblies? I.e. Gibt es detailliertere Funktionsaufrufe in diesem mehr als 500 Zeilen umfassenden Behemoth, die Sie testen könnten?

+0

In der Tat gibt es mehr granulare Funktionen, aber wie kann ich die Unit-Unit testen, ohne zuerst den Legacy-Code zu refactorieren, um die granulareren Methoden zu extrahieren? –

+0

Das ist die $ 64.000 Frage. Und manchmal ist die Antwort ein Kompromiss. Sie möchten nach einem perfekten Refactoring streben, aber manchmal müssen Sie schreiben, welche Unit-Tests Sie verwenden können/während/Sie den Legacy-Code verbessern. Zumindest war das meine Erfahrung. – JMD

1

Aus meiner Erfahrung würde ich Tests nicht für bestimmte Methoden in den Legacy-Code schreiben, sondern für die gesamte Funktionalität, die es bietet. Diese können den vorhandenen Methoden entsprechen oder auch nicht.

1

Schreiben Sie Tests auf was auch immer Ebene des Systems können Sie (wenn Sie können), wenn dies bedeutet, eine Datenbank usw., dann so sei es.Sie müssen viel mehr Code schreiben, um zu bestätigen, was der Code gerade tut, da eine 500-Zeilen + -Methode möglicherweise viel Verhalten enthalten hat. Wenn man die alten gegen die neuen vergleicht, wenn man die Tests gegen den alten Code schreibt, gehen sie durch und sie decken alles ab, was sie tut, wenn man sie gegen den neuen Code laufen lässt, prüft man effektiv das alte gegen das neue. Ich tat dies, um einen komplexen sql Trigger zu testen, den ich umgestalten wollte, es war ein Schmerz und nahm sich Zeit, aber einen Monat später, als wir ein anderes Problem in diesem Bereich fanden, war es wert, die Tests dort zu haben.

9

Für Anweisungen zum Refactoring von Legacy-Code möchten Sie vielleicht das Buch Working Effectively with Legacy Code lesen. Es gibt auch eine kurze PDF-Version here.

+0

Schön, den Link zum PDF zu haben - nette Arbeit! – MarkJ

+0

+1 Dieses Buch beantwortet genau die Frage. –

1

Nach meiner Erfahrung ist dies die Realität bei der Arbeit an Legacy-Code. Book (Working with Legacy), von Esko erwähnt, ist eine ausgezeichnete Arbeit, die verschiedene Ansätze beschreibt, die Sie dorthin führen können.

Ich habe ähnliche Probleme mit out-Unit-Test selbst gesehen, die zu System/Funktionstest gewachsen ist. Das Wichtigste bei der Entwicklung von Tests für Legacy- oder vorhandenen Code ist die Definition des Begriffs "Einheit". Es kann sogar eine funktionale Einheit wie "Lesen aus der Datenbank" usw. sein. Identifizieren Sie wichtige Funktionseinheiten und führen Sie Tests durch, die einen Mehrwert schaffen.

Nebenbei gab es kürzlich Gespräche zwischen Joel S. und Martin F. über TDD/Unit-Tests. Mein Standpunkt ist, dass es wichtig ist, Einheit zu definieren und sich darauf zu konzentrieren! URLS: Open Letter, Joel's transcript und podcast

0

Das folgende Buch: The Art of Unit Testing ein paar Kapitel mit einigen interessanten Ideen enthält, wie mit Legacy-Code in Bezug auf die Entwicklung von Unit-Tests umgehen.

Ich fand es sehr hilfreich.