Hier mein Ansatz. Es ist zeitaufwändig, da es sich um einen Refactor-Test in 4 Phasen handelt.
Was ich herausstellen werde, passt möglicherweise besser zu Komponenten mit höherer Komplexität als die im Beispiel der Frage herausgestellten.
Auf jeden Fall gilt die Strategie für alle Komponentenkandidaten, die über eine Schnittstelle (DAO, Services, Controller, ...) normalisiert werden sollen.
1. Die Schnittstelle
Sammeln Sie alle öffentlichen Methoden von MyDocumentService und fügen Sie sie in einer Schnittstelle zusammen. Zum Beispiel. Wenn es bereits existiert, verwenden Sie dieses, anstatt eines neuen zu setzen .
public interface DocumentService {
List<Document> getAllDocuments();
//more methods here...
}
Dann zwingen wir MyDocumentService , diese neue Schnittstelle zu implementieren.
So weit, ist es gut. Es wurden keine wesentlichen Änderungen vorgenommen, wir haben den aktuellen Vertrag eingehalten und das Verhalten bleibt unberührt.
public class MyDocumentService implements DocumentService {
@Override
public List<Document> getAllDocuments(){
//legacy code here as it is.
// with no changes ...
}
}
2. Komponententest des Legacy-Codes
Hier haben wir die harte Arbeit. So richten Sie eine Testsuite ein Wir sollten so viele Fälle wie möglich festlegen: erfolgreiche Fälle und auch Fehlerfälle. Letztere dienen der Qualität des Ergebnisses.
Anstatt MyDocumentService zu testen, verwenden wir jetzt die Schnittstelle als zu testenden Vertrag.
Ich werde nicht auf Details eingehen, also vergib mir, wenn mein Code zu einfach oder zu agnostisch aussieht
public class DocumentServiceTestSuite {
@Mock
MyDependencyA mockDepA;
@Mock
MyDependencyB mockDepB;
//... More mocks
DocumentService service;
@Before
public void initService(){
service = MyDocumentService(mockDepA, mockDepB);
//this is purposed way to inject
//dependencies. Replace it with one you like more.
}
@Test
public void getAllDocumentsOK(){
// here I mock depA and depB
// wanted behaivors...
List<Document> result = service.getAllDocuments();
Assert.assertX(result);
Assert.assertY(result);
//... As many you think appropiate
}
}
Diese Phase dauert länger als jede andere in diesem Ansatz. Und es ist das wichtigste, weil es den Bezugspunkt für zukünftige Vergleiche setzt.
Hinweis: Da keine wesentlichen Änderungen vorgenommen wurden, bleibt das Verhalten unberührt. Ich schlage vor, hier einen Tag in das SCM einzufügen. Tag oder Zweig spielt keine Rolle. Mach einfach eine Version.
Wir wollen es für Rollbacks, Versionsvergleiche und möglicherweise für die parallele Ausführung des alten und des neuen Codes.
3. Refactoring
Refactor wird in eine neue Komponente implementiert. Wir werden keine Änderungen am bestehenden Code vornehmen. Der erste Schritt ist so einfach wie das Kopieren und Einfügen von MyDocumentService und das Umbenennen in CustomDocumentService (zum Beispiel).
Neue Klasse implementiert weiterhin DocumentService . Dann gehen Sie und refactorize getAllDocuments () . (Fangen wir mit eins an. Pin-Refactors)
Möglicherweise müssen einige Änderungen an der DAO-Schnittstelle / den DAO-Methoden vorgenommen werden. Wenn ja, ändern Sie den vorhandenen Code nicht. Implementieren Sie Ihre eigene Methode in der DAO-Schnittstelle. Kommentieren Sie den alten Code mit " Veraltet" und Sie werden später wissen, was entfernt werden sollte.
Es ist wichtig, die vorhandene Implementierung nicht zu beschädigen oder zu ändern. Wir wollen beide Dienste parallel ausführen und dann die Ergebnisse vergleichen.
public class CustomDocumentService implements DocumentService {
@Override
public List<Document> getAllDocuments(){
//new code here ...
//due to im refactoring service
//I do the less changes possible on its dependencies (DAO).
//these changes will come later
//and they will have their own tests
}
}
4. Aktualisieren von DocumentServiceTestSuite
Ok, jetzt der einfachere Teil. Hinzufügen der Tests der neuen Komponente.
public class DocumentServiceTestSuite {
@Mock
MyDependencyA mockDepA;
@Mock
MyDependencyB mockDepB;
DocumentService service;
DocumentService customService;
@Before
public void initService(){
service = MyDocumentService(mockDepA, mockDepB);
customService = CustomDocumentService(mockDepA, mockDepB);
// this is purposed way to inject
//dependencies. Replace it with the one you like more
}
@Test
public void getAllDocumentsOK(){
// here I mock depA and depB
// wanted behaivors...
List<Document> oldResult = service.getAllDocuments();
Assert.assertX(oldResult);
Assert.assertY(oldResult);
//... As many you think appropiate
List<Document> newResult = customService.getAllDocuments();
Assert.assertX(newResult);
Assert.assertY(newResult);
//... The very same made to oldResult
//this is optional
Assert.assertEquals(oldResult,newResult);
}
}
Jetzt haben wir oldResult und newResult beide unabhängig validiert, aber wir können auch miteinander vergleichen. Diese letzte Validierung ist optional und vom Ergebnis abhängig. Vielleicht ist es nicht vergleichbar.
Es mag nicht zu viel Sinn machen, zwei Sammlungen auf diese Weise zu vergleichen, aber es wäre für jede andere Art von Objekt gültig (Pojos, Datenmodellentitäten, DTOs, Wrapper, native Typen ...)
Anmerkungen
Ich würde es nicht wagen zu sagen, wie man Unit-Tests macht oder wie man Schein-Bibliotheken benutzt. Ich traue mich auch nicht zu sagen, wie Sie den Refactor machen müssen. Ich wollte eine globale Strategie vorschlagen. Wie Sie vorankommen, hängt von Ihnen ab. Sie wissen genau, wie Code ist, seine Komplexität und ob eine solche Strategie einen Versuch wert ist. Fakten wie Zeit und Ressourcen sind hier wichtig. Auch darauf kommt es an, was Sie in Zukunft von diesen Tests erwarten.
Ich habe meine Beispiele durch einen Dienst gestartet und würde mit DAO und so weiter folgen. Ein tiefer Einblick in die Abhängigkeitsstufen. Mehr oder weniger könnte man es als Up-Bottom- Strategie bezeichnen. Bei geringfügigen Änderungen / Umgestaltungen ( wie im Tour-Beispiel ) würde ein Bottom-up- Ansatz die Aufgabe erleichtern. Weil der Umfang der Änderungen gering ist.
Schließlich liegt es an Ihnen, veralteten Code zu entfernen und alte Abhängigkeiten auf die neuen umzuleiten.
Entfernen Sie auch veraltete Tests und die Arbeit ist erledigt. Wenn Sie die alte Lösung mit ihren Tests versioniert haben, können Sie sie jederzeit überprüfen und miteinander vergleichen.
Infolge so vieler Arbeiten haben Sie Legacy-Code getestet, validiert und versioniert. Und neuer Code, getestet, validiert und bereit zur Versionierung.