1
Orientation in Objects GmbH
Weinheimer Str. 68
68309 Mannheim
www.oio.de
TDLCRTest Driven Legacy Code Refactoring
JUG Hannover
15.03.2017
1.0
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Über mich
2
Falk Sippach (@sippsack)
Trainer, Berater, Entwickler
SchwerpunkteArchitektur
Agile SoftwareentwicklungCodequalitätCo-Organisator
2
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Java, XML und Open Source seit 1998
) Competence Center)) Object Rangers )
• Schulungen, Coaching,
Weiterbildungsberatung,
Train & Solve-Programme
• Methoden, Standards und
Tools für die Entwicklung
von offenen, unternehmens-
weiten Systemen
• Unterstützung laufender
Java Projekte
• Perfect Match
• Rent-a-team
• Coaching on the project
• Inhouse Outsourcing
• Schlüsselfertige Realisierung
von Java Software
• Individualsoftware
• Pilot- und Migrationsprojekte
• Sanierung von Software
• Software Wartung
) Software Factory )
3
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Abstract
4
Bestandsanwendungen müssen gewartet und ggf. weiterentwickelt
werden, bergen aber meist viele Defekte. Als Entwickler fürchten wir uns
zudem, mehr Schaden anzurichten, weil das Verständnis für den Legacy
Code fehlt. Refactoring kann zum Verstehen beitragen, endet aber
aufgrund der typischerweise fehlenden automatisierten Tests in einem
Blindflug.
Mit testgetriebener Entwicklung scheint es eine Allzweckwaffe für gutes
Design und eine geringe Fehlerrate zu geben. Aber TDD und Legacy
Code scheinen sich eigentlich auszuschließen. Anhand von Live Coding
schauen wir, wie die testgetriebene Entwicklung trotzdem helfen kann,
den Code ohne allzu große Bauchschmerzen anzupassen. Schöner
Nebeneffekt wird das Entstehen eines automatisierten Testbetts sein,
welches zukünftige Refactorings leichter machen wird.
3
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Gliederung
• TDD by Example
• Was ist Legacy Code?
• Test Driven Legacy Code Refactoring
• Bestandscode testen
5
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 6
Hugo
Dieter
Kenn'se noch
den Meier?
Genau, der ist in
Rente gegangen.
Der hat doch die
alte Telefon-Tarif-
Anwendung gewartet.
Brauche aber ein
neues Feature!
4
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 7
Hugo
Dieter
Wir wollen den Kunden
Rabatte gewähren können.
Bauen Sie mir das
bis nächste Woche ein!
Ähm, in 3 Tagen?
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 8
Hugo
Dieter
?
Und bitte nichts kaputt machen, das ist
eine unternehmenskritische Anwendung!
5
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 9
1
2
3
0 Neue Funktionalität mit TDD
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 10
TDLCRTest Driven Development
6
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
TDD hilft beim Verbessern des Codes
11
https://mobile.twitter.com/TDDQuotes/status/783787123237265408
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 12
RED
GREENREFACTOR
Test
Driven
Development
0
7
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
"Gesetze" von TDD
• Schreibe einen fehlschlagenden Test, bevor du
Code für das Produktivsystem verfasst.
• Schreibe nur so viel Code für den Test, damit er
kompiliert (Rot)
• Schreibe nur so viel Code, um den Test zu
bestehen (Grün)
• Refactor: Duplikation entfernen
13
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 14
Foto von StockSnap: https://pixabay.com/de/codierung-gesch%C3%A4ft-arbeiten-macbook-699318/ (CC0 Public Domain Lizenz)
Live-Coding
Rabatt-Klasse mit Prozentsatz
10 % Rabatt => berechnePreis
liefert 90 % vom Preis zurück
kein Rabatt => berechnePreis
liefert gleichen Wert zurück
ignoriere negative Rabatte =>
berechnePreis liefert gleichen
Wert zurück
8
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Tipps TDD
• kleine Schritte
• Workflow (Red – Green - Refactor) einhalten
• Ideen für weitere Testfälle in eine Liste schreiben und nach und
nach abarbeiten (kleine Schritte!)
15
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Vorteile TDD
• kein ungetesteter Code => sehr hohe Testabdeckung
• saubere/testbare Architektur durch TDD als Designstrategie
– Anforderungen werden durch Tests dokumentiert
– Software weniger fehleranfällig und wartungsintensiv
• hohe Qualität und verständlicher Code durch ständiges Aufräumen
– Entfernen von Duplikationen
– Abstrahieren wo sinnvoll
– Einhalten der Code-Konventionen
• kein unnötiger Code auf Vorrat (YAGNI)
– Konzentration auf das Wesentliche
16
9
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 17
TDLCRLegacy Code
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 18
Foto von smpcas, CC0 Public Domain Lizenz, https://pixabay.com/de/pula-kroatien-amphitheater-erbe-827909/
Vermächtnis
Erbe
Altlast
Hinterlassenschaft
10
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 19
// TODO: refactor thisGreenfield, Agile, TDD, …
Foto von John Hult, CC0 1.0 Universal,
https://unsplash.com/search/ruin?photo=uh0u8OH4zuE
Foto von Antranias, CC0 Public Domain Lizenz,
https://pixabay.com/de/bauarbeiten-ger%C3%BCst-baustelle-670278/
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 20
"Some crap made by someone else"
Alter Code, geerbter Code, Code
den niemand mehr versteht …
Code entstanden unter Zeitdruck
und ohne automatisierte Tests
Technische Schulden
Was ist
Legacy Code?
Was ist mit unserem
eigenen Code?
Foto von John Hult, CC0 1.0 Universal,
https://unsplash.com/search/ruin?photo=uh0u8OH4zuE
11
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 21
Legacy code is
valuable code
that we feel afraid
to change.
J. B. Rainsberger
Foto von PublicDomainPictures, CC0 Public Domain Lizenz, https://pixabay.com/de/menschen-abdeckung-schrei-314481/
“
http://agileprague.com/speakers/speakers/j-b-rainsberger.htm
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 22
Code
without tests
is bad code.Michael Feathers
“
Legacy Code Dilemma
http://computersweden.idg.se/2.2683/1.304186/kraven-okar-pa-valutformade-program
12
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 23
um Tests zu schreiben muß der Code testbar sein, um testbaren Code
zu erhalten müßte man refactoren, um zu refactoren könnte man Tests
schreiben, um Tests zu schreiben muß der Code testbar sein, um
testbaren Code zu erhalten müßte man refactoren, um zu refactoren
könnte man Tests schreiben, um Tests zu schreiben muß der Code
testbar sein, um testbaren Code zu erhalten müßte man refactoren,
um zu refactoren könnte man Tests schreiben, um Tests zu
schreiben muß der Code testbar sein, um testbaren Code zu erhalten
müßte man refactoren, um zu refactoren könnte man Tests schreiben,
um Tests zu schreiben muß der Code testbar sein, um testbaren Code
zu erhalten müßte man refactoren, um zu refactoren könnte man Tests
schreiben, um Tests zu schreiben muß der Code testbar sein, um
testbaren Code zu erhalten müßte man refactoren, um zu refactoren
könnte man Tests schreiben, um Tests zu schreiben muß der Code
testbar sein, um testbaren Code zu erhalten müßte man refactoren, um
zu refactoren könnte man Tests schreiben, um Tests zu schreiben muß
der Code testbar sein, um testbaren Code zu erhalten müßte man refact
Die Katze beißt sich
in den Schwanz!
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 24
refactoren bräuchte man Tests, Tests würden helfen, Code zu
verstehen, um Code zu verstehen könnte man Code refactoren, um
Code zu refactoren bräuchte man Tests, Tests würden helfen, Code zu
verstehen, um Code zu verstehen könnte man Code refactoren, um
Code zu refactoren bräuchte man Tests, Tests würden helfen, Code zu
verstehen, um Code zu verstehen könnte man Code refactoren, um
Code zu refactoren bräuchte man Tests, Tests würden helfen, Code zu
verstehen, um Code zu verstehen könnte man Code refactoren, um
Code zu refactoren bräuchte man Tests, Tests würden helfen, Code zu
verstehen, um Code zu verstehen könnte man Code refactoren, um
Code zu refactoren bräuchte man Tests, Tests würden helfen, Code zu
verstehen, um Code zu verstehen könnte man Code refactoren, um
Code zu refactoren bräuchte man Tests, Tests würden helfen, Code
zu verstehen, um Code zu verstehen könnte man Code refactoren, um
Code zu refactoren bräuchte man Tests, Tests würden helfen, Code zu
verstehen, um Code zu verstehen könnte man Code refactoren, um
Code zu refactoren bräuchte man Tests, Tests würden helfen, Code zu
Henne-Ei-
Problem
Foto von Myriams-Fotos, CC0 Public Domain Lizenz, https://pixabay.com/de/k%C3%BCken-gelb-glucke-henne-1433025/
13
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Foto von John Hult, CC0 1.0 Universal,
https://unsplash.com/search/ruin?photo=uh0u8OH4zuE
25
Verstehen
Erweitern
Bugfixing
Warum
Legacy Code
anfassen?
Optimierung
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Foto von John Hult, CC0 1.0 Universal,
https://unsplash.com/search/ruin?photo=uh0u8OH4zuE
26
Verstehen
Erweitern
Bugfixing
Wo kann TDD
bei Legacy
Code helfen?
Optimierung
14
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 27
TDD vs. Legacy Code
Test vor dem Code vs.
Code schon da
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 28
TDLCRTest Driven Legacy Code Refactoring
15
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 29
Erweitern Strategie 1:
Edit and Pray!
Foto von Myriams-Fotos: https://pixabay.com/en/rosary-faith-pray-folded-hands-1211064/ (CC0 Public Domain Lizenz)
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 30
Erweitern Strategie 2:
Canary in the
coal mine
Foto von janjf93: https://pixabay.com/en/canary-bird-warrant-canary-road-1606695/ (CC0 Public Domain Lizenz)
16
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 31
1
2
3
Sicherheitsnetz
Testen des Bestands-Codes
Integration neue Funktion
0 Neue Funktionalität mit TDD
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 32
Foto von bella67: https://pixabay.com/de/spinnennetz-mit-wasserperlen-netz-921039/ (CC0 Public Domain Lizenz)
Sicherheitsnetz
1
17
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 33
Foto von istara: https://pixabay.com/de/gold-bar-goldbarren-reich-geld-296115/ (CC0 Public Domain Lizenz)
Golden Master
gegenwärtiges Verhalten
dokumentieren und erhalten
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 34
18
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Konsolenausgaben abprüfen
35
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 36
Foto von StockSnap: https://pixabay.com/de/codierung-gesch%C3%A4ft-arbeiten-macbook-699318/ (CC0 Public Domain Lizenz)
Live-Coding
19
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Was mache ich bei GUI-Anwendungen?
37
public static void main(String... args) throws Exception {
WebDriver driver = new FirefoxDriver();
driver.get("http://www.retest.de");
while (true) {
List<WebElement> links = driver.findElements(By.tagName("a"));
links.get(random.nextInt(links.size())).click();
Thread.sleep(500);
List<WebElement> fields =
driver.findElements(By.xpath("//input[@type='text']"));
WebElement field = fields.get(random.nextInt(fields.size()));
field.sendKeys(randomString());
Thread.sleep(500);
}
}
https://entwicklertag.de/frankfurt/2016/sites/entwicklertag.de.frankfurt.2016/files/slides/Bei%20uns%20testen%20lauter%20Affen_0.pdf
1
2 3
4
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 38
1
2
3
Sicherheitsnetz
Testen des Bestands-Codes
Integration neue Funktion
0 Neue Funktionalität mit TDD
20
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 39
Neue
Features
ausliefern …
2
Foto von PublicDomainPictures: https://pixabay.com/en/adorable-baby-basket-beautiful-boy-21259/ (CC0 Public Domain Lizenz)
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Aufhören Legacy Code zu schreiben!
• sonst wird die Codebasis nur schlimmer und man entfernt sich
immer mehr davon, jemals Tests hinzuzufügen
• Keine neuen Features mehr ohne Unit-Testing!
• Sprout Method + Wrap Method als Hilfen
40
21
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Sprout Method
public int berechne(int a, int b) {
int c = a + b;
// weitere wichtige Aufgaben
// neues Verhalten
c = verdoppeleResult(c);
// weitere wichtige Aufgaben
return c;
}
protected int verdoppeleResult(int result) {...}
41
Aufruf aus
Bestandscode
neue Methode,
testgetrieben
entwickeln
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Sprout Method und TDD (1)
// nicht kompilierenden Test schreiben
@Test
public void testVerdoppeleResult() {
assertEquals(2, rechner.verdoppeleResult(1);
}
// Kompilerfehler beheben
protected int verdoppeleResult(int result) {
return 0;
}
// Test ist rot
42
22
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Sprout Method und TDD (2)
// einfachste Lösung, damit Test grün wird
protected int verdoppeleResult(int result) {
return 2;
}
// nichts zu refactoren, von vorn beginnen
@Test
public void testVerdoppeleResult() {
assertEquals(2, rechner.verdoppeleResult(1);
assertEquals(4, rechner.verdoppeleResult(2);
}
// Test wieder rot, Lösung generalisieren
43
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Wrap Method
• ähnlich zu Sprout Method, neues Verhalten aber vor oder am Ende der zu ändernden Methode
public int berechne(int a, int b) {
logResult(c);
return berechnePrivate(a, b);
}
private int berechnePrivate(int a, int b) {
int c = a + b;
// weitere wichtige Aufgaben
return c;
}
protected void logResult(int result) {...}
44
23
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 45
Foto von StockSnap: https://pixabay.com/de/codierung-gesch%C3%A4ft-arbeiten-macbook-699318/ (CC0 Public Domain Lizenz)
Live-Coding
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Zusammenfassung: Sprout Method und TDD
• Test schreiben vor der Integration der neuen Funktionalität
– Red – Green – Refactor
• alte Funktionalität wird gleich mitgetestet
– Testbett wird bei jeder Erweiterung besser
• uninteressante Abhängigkeiten durch Mocks ersetzen
46
24
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 47
1
2
3
Sicherheitsnetz
Testen des Bestands-Codes
Integration neue Funktion
0 Neue Funktionalität mit TDD
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 48
Bestandscode testen, aber:
3
Foto von geralt: https://pixabay.com/en/board-school-uni-learn-work-test-361516/ (CC0 Public Domain Lizenz)
teuer und langwierig
Code meist kaum/nicht testbar
kein TDD möglich
25
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 49
1. Identify what to change
2. Identify what to test
3. Break dependencies
4. Write the tests
5. Modify and refactoring
Foto von olafpictures: https://pixabay.com/en/germany-hermekeil-damphlok-museum-1349350/ (CC0 Public Domain Lizenz)
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 50
Seam (Nahtstelle)Ein Seam ist eine Stelle, an der man das Verhalten editieren kann, ohne direkt an
dieser Stelle zu ändern.
Aufbrechen stark gekoppelter Abhängigkeiten aka Subclass and Override
aka Extract and Override Call
aka … (weitere verschiedene Varianten)
26
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Subclass and Override
• es gibt (protected) Methoden für die externen Dependencies
• in einer Subklasse mit leerem oder speziellem Inhalt überschreiben
• Verwenden der Subklasse in den Tests
public void saveOrder(int orderId) {
Order order = orderRepository.getOrderById(orderId);
getOrderChanges();
saveOrderToFile(order);
}
protected void saveOrderToFile(Order order) {
// File IO
}
51
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Subclass and Override (2)
public class OrderServiceForTests extends OrderService {
@Override
protected void saveOrderToFile(Order order) {
// nichts tun (keine IO, keine externen Dependenies)
// oder order-Parameter extern überprüfbar machen
}
}
52
27
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Extract and Override Call
• zu überschreibenden Code zunächst herauslösen (Extract Method)
public void sendOrderConfirmation(int orderId) {
Order order = orderRepo.getOrderById(orderId);
Mail email = new MailMessage(defaultSender,
subject, ...));
smtpClient.send(email);
}
53
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Extract and Override Call
• dann wie bei Subclass and Override
public void sendOrderConfirmation(int orderId) {
Order order = orderRepo.getOrderById(orderId);
Mail email = new MailMessage(defaultSender,
subject, ...));
sendEmail(email);
}
protected void sendEmail(Mail email) {
smtpClient.send(email);
}
54
28
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Extract Pure Method/Function
• Codestellen isolieren
• Ziel: separat testen, Duplikation reduzieren
• Vorteil von reinen Methoden: seiteneffektfrei, keine Statusänderung
• Vorsicht: wir müssen Code ändern bevor Tests da sind
– immer kleine Schritte, Tools/IDEs für das Refactoring verwenden
– Extract Method ist typischerweise sicher
55
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Extract Interface
• Klasse extrahieren (Methode verschieben)
• Interface herausziehen
• Aufrufer ändern und das Interface verwenden
• leichter austauschbar gegen Dummy, Stub, Mock
56
29
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Viele weitere Refactorings für Legacy Code
57
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Weitere Tools/Hilfsmittel
• private Methoden testen
– nicht schön, aber praktisch
– per Reflection oder mit Frameworks
– z. B. private Pure- und Sprout-Methoden testen
• Mocking
– Extract and Override und noch mächtiger
– Interaktion mit Umgebung testen
– erwartete Parameter und Aufrufreihenfolge sicherstellen
58
30
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Zusammenfassung
• Sicherheitsnetz spannen: Golden Master
• Legacy Code refactoren => Ziel testbarer Code
– Dependencies aufbrechen
– Codestellen separieren
• Tests immer hinzufügen bei
– Bugfixing
– Erweitern
• TDD konsequent beim Schreiben von neuem Code und auch bei
der Aufruf-Integration von neuem Code
• nach und nach alten Code ebenfalls mit Unit-Tests versehen
59
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Tools
60
Code Coverage
Approval Tests
Infinitest
Eclipse Metrics
31
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH 61
Legacy Code Retreat
Foto von Jmabel: https://commons.wikimedia.org/wiki/File:Seattle_-_Budokan_Dojo_judo_demo_04.jpg?uselang=de (CC BY-SA 3.0 Lizenz)
https://github.com/jbrains/trivia
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Links
• Code-Beispiel der Live-Demo
– https://github.com/sippsack/BadTelefon-Test-Driven-Legacy-Code-
Refactoring
• anderes Code-Beispiel für Legacy Code
– https://github.com/jbrains/trivia
• Blog: Techniken zu Legacy Code-Retreat
– http://blog.adrianbolboaca.ro/2014/04/legacy-coderetreat/
62
32
Test Driven Legacy Code Refactoring© Orientation in Objects GmbH
Literaturhinweise
63
• Refactoring
– Sprache: Englisch
Gebunden - 464 Seiten - Addison Wesely
Erscheinungsdatum: 1. Juni 1999
ISBN: 0201485672
• Working Effectively with Legacy Code
– Sprache: Englisch
– Gebunden
Orientation in Objects GmbH
Weinheimer Str. 68
68309 Mannheim
www.oio.de
??
? ?
????
Fragen ?
33
Orientation in Objects GmbH
Weinheimer Str. 68
68309 Mannheim
www.oio.de
Vielen Dank für Ihre
Aufmerksamkeit !