Jag har lekt lite med TDD (Test Driven Development) vilket har varit ganska intressant. Att skriva testdriven kod är först och främst en vanefråga, att vänja sig vid ett sätt att tänka. Det är en sorts förändring som jag tror kräver en hel del övning innan man börjar försöka att applicera tekniken på skarpa projekt där man faktiskt ska leverera kundnytta. Utan någon form av ledarskap och erfarenhet att luta sig mot finns det många fällor man kan hoppa in i och därmed göra livet betydligt svårare än det behöver vara.
Test driven utveckling följer en cykel med tre steg
1) Fundera ut ett test och implementera det i testkoden, testet ska falera.
2) Implementera produktionskod för att få testet att fungera
3) Refaktorera, både produktionskod och testkod.
När man skriver testet ska man försöka att göra det så litet som möjligt. Tanken är inte att testet ska täcka in allt som kan inträffa utan enbart ett scenario. Man ska alltså skriva så lite som möjligt för att åstadkomma ett falerande test. Det får till följd att en serie av tester kan ses som en specifikation av programmet du skriver. Varje nytt test bygger på de tidigare. Varje nytt test beskriver en liten bit ny funktionalitet. När man läser en testklass ska man intoduceras i problemet och test för test få de mer komplicerade användningsområdena presenterade.
När man skriver produktionskod ska man inrikta dig på att få testet att gå igenom med minsta möjliga insats. Även om du kan lösa tusen problem genom att implementera ett helt framework i din produktionskod så behöver du sannolikt inte det för att få ett test att gå igenom. De första testerna löser man sannolikt genom att returera null, eller textkonstanter i produktionskoden. Syftet med att man vill lösa minsta möjliga del av problemet är att man ofta vill ta fram lösningar som är betydligt komplexare än vad man faktiskt har behov av. Genom att lösa ett test i taget med minsta möjliga förändring går man inte händelserna i förväg. Många problem har väldigt enkla lösningar om man kan undvika att börja med att bygga stora arvsheirakier och domänmodeller.
Det tredje och sista steget är att refaktorera koden man har skrivit. Någon skrev att man ska genomföra detta steg skoningslöst. Med testerna som stöd för refaktoreringen så får man snabbt reda på om man har förstört någon existerande funktionallitet och genomgripande förändringar i koden. Men man ska också refaktorera sina tester. Det är förmodligen viktigare att testerna är i bra skick än produktionskoden.
Jag har gjort tre små experiment med TDD så här långt. PrimfactorsKata, BowlingGameKata och så en PostfixMiniräknare. Det jag har lärt mig är att man bör ha ganska bra koll på problemet man löser. För skriver du dina tester fel är det inte riktigt lika lätt att ändra i dem som det är att ändra i produktionskoden. Tex missuppfattade jag reglerna för hur man beräknar poäng i BowlingGameKatan vilket inte var helt smidigt att rätta till. Däremot att byta ut den rekursiva kod jag använde i PrimefactorsKatan till en algoritmisk dito gjorde med lätthet, testerna talade om när något inte var som det skulle och testerna förändrades inte bara för att jag bytte lösning, api:et var det samma. Så om man har en god idé om hur api:et ska se för det problem man ska lösa är TDD utmärkt. Om man har mer svävande uppfattning om vilket api problemet döljer sig bakom får man nog ganska omfattande förändringar i både tester och i produktionskod, något som gör livet lite besvärligare.
En lösning för att ta fram vilket api man vill utgå från i testerna kan vara att göra en prototyp där man kan experimentera sig fram till hur problemets api ser ut. Man behöver inte implementera problemet i detalj men man kan testa ett par möjliga gränssnitt mot problemet. När man sedan har sin idé om hur man vill använda funktionen kan man börja implementera med hjälp av TDD.
Nu skulle säkert en renlärig TDD:are säga att det api man tagit fram utan TDD inte är optimalt eller att man har försökt att lösa problemen i fel ordning. För om du utgår från sådant du inte kan ändra, tex ett utseendet på en miniräknare så har du inte så stora möjligheter att ändra API:et. Sedan för varje steg du jobbar dig ner i lagren av kod så skulle api:et redan mer eller mindre vara förutbestämt. Jag kan köpa in på det resonemanget också men har inte hittills någon erfarenhet av att det faktiskt fungerar på det sättet utan skulle behöva få tag på ett lämpligt scenario att testa det på. De jag har gjort hittills har ju varit av ganska trivial natur för att begränsa tidsåtgången.