Testdriven utveckling Januari 2009, KTH Alexander Tarnowski Teorin bakom testdriven utveckling Bakgrund Testdriven utveckling började nämnas kring 1999-2000 av Kent Beck I praktiken implementationen av XP:s test first Målet är Clean code that works Ses som ett verktyg för hantering av rädsla och osäkerhet i utvecklingsprocessen Baserat på enhetstest (unit tests) 1
Testdriven utveckling och XP Värderingar Kommunikation Enkelhet Feedback Mod Respekt Praktiker Sitta tillsammans Teamet Informativ arbetsplats Energized work Parprogrammering Stories Veckocykeln Principer Kvartalscykeln Slack Bygga på 10 minuter Continuous integration Testa först Inkrementell design TDD strävar efter: Clean code that works Ron Jeffries Förutsägbarhet. Ingen projektsvans! Dina kolleger litar på dig, och du på dem Lär dig allt du kan av koden Omedelbar feedback Kul att skriva Förutsägbarhet 2
TDD reducerar: rädsla (och osäkerhet) Rädsla gör oss osäkra Rädsla får oss att kommunicera mindre Rädsla påverkar vår förmåga att ta emot feedback Rädsla gör oss otrevliga TDD bygger på unit tests Enhetstester... Visar att isolerade komponenter av program fungerar Är isolerade från varandra Och är inte... Prestandatester Stresstester Användbarhetstester <Stoppa in favorit-buzzword som slutar med tester här> Metodologin Skriv ett test som misslyckas Få testet att köra Ta bort duplicering 3
TDD:s utvecklingscykel Att skriva ny kod endast om ett automatiserat test misslyckas Och därefter ta bort duplicering Leder till och kräver: Organisk design: körbar kod ger feedback och driver utvecklingen framåt Design med high cohesion och loose coupling Att utvecklarna måste skriva sina egna test Utvecklingsmiljön måste ge snabb feedback Vad är ett test Testa = utvärdera Vilken logik testar vi? Vilka testdata väljer vi? Bäst att upprepa: tester ska vara oberoende! Vilken logik testar vi Testa: Villkor Iterationer Sekventiella operationer Polymorfism Testa inte: Tredje parts kod (t ex att JDBC eller en applikationsserver fungerar) Too simple to break 4
Vilka testdata väljer vi? Enkla assertequals(5, calc.plus(2, 3)); mot assertequals(12517294, calc.plus(11286060, 1231234)); Relevanta @Test(expected=NullPointerException.class) public void testinsert() { insertintodatabase(null); } kanske inte är bästa testet för void insertintodatabase(transferobject o) {... } Självförklarande final int addend = 2; final int augend = 3; assertequals(addend + augend, calc.plus(addend, augend)); Testtekniker Vi tar oss från rött till grönt genom att: Fejka Uppenbara implementationen Triangulering Teknik 1: Fejka TestCalculator.java assertequals(4, calculator.plus(3,1)); Calculator.java int plus(int augend, int addend) { return 4; } 5
Teknik 2: Uppenbara implementationen Man får faktiskt göra så TestCalculator.java assertequals(4, calculator.plus(3,1)); Calculator.java int plus(int augend, int addend) { return augend + addend; } Teknik 3: Triangulering Generalisera om vi har två eller fler fall Använd i svårare fall! TestCalculator.java assertequals(4, calculator.plus(3,1)); assertequals(8, calculator.plus(4,4)); Calculator.java int plus(int augend, int addend) { return???; } Dåliga tester Mycket setupkod = för stora och komplicerade objekt Duplicerad setup = för många objekt, för hårt kopplade Långa test = kommer inte att köras, åldras och blir fel 6
Varför reducera duplicering? Urholkar den konceptuella integriteten Försvårar underhåll Vanligaste exemplet: duplicerad logik Näst vanligaste: copy n paste kod Beroende är sjukdomen, dupliceringen symptomet För en gångs skull! Eliminerar vi symptomet (dupliceringen) minskar vi beroendet (sjukdomen) Hur stora steg ska man ta? Test transformerar ett abstrakt problem till att få testet att funka Storleken på stegen beror på osäkerheten Vi slutar testa när rädslan och osäkerheten förvandlas till leda TDD handlar inte om att ta små steg, utan om att kunna göra det Svårt att driva med tester Säkerhetsprogramvara Parallella system Realtidsprogramvara 7
Argument mot TDD och utvecklardriven testning Tester tar lång tid att skriva Tester tar lång tid att köra En del kod måste testas live När man inte vet vad koden ska göra, så kan man inte testa if it compiles then it works Utvecklare är inte testare! Sammanfattning Vi får tester att köra genom fejk, triangulering, eller den uppenbara implementationen Borttagning av dupliceringen mellan test och skarp kod driver designen Flytande gräns mellan hur stora stegen mellan test och implementation blir beroende av svårighetsgraden. TDD ersätter: Code for today, design for tomorrow med By not considering the future of your code, you make your code much more likely to be adaptable in the future Bortom TDD Behavior-driven design Acceptanstestdriven utveckling 8
Behavior-driven design Publicerades kring slutet av 2007 Order test i testdriven utveckling ställer till med problem Allomfattande språk för analysprocessen: Stories: As a/i want/so that Acceptanskriteria: Given/when/then Acceptanstestdriven utveckling Ramverk: Concordion, FitNesse Uppbyggda av en specifikation skriven i klartext och en fixtur Specifikationen innehåller parametrar till testfixturen och kommunicerar testets resultat Att införa TDD: de största problemen Nytt arbetssätt Inlärningskurvorna för teknikerna och verktygen Legacykod Man ser inte nyttan omedelbart 9
Nytt arbetssätt Kräver disciplin Kräver kunskap: Lätt att testa fel saker Lätt att testa saker fel Om det enda man har är en hammare, blir varje problem en spik En möjlig inlärningskurva produktivitet? Valfritt ramverk Databastestning/webbtestning Refactoring Testning av kollaboratörer tid Teori JUnit Problem med legacykod Inga eller få tester Affärslogik i databasen Felimplemterade J2EE patterns Singletons M m, m m Refactoring! 10
Hur vi underlättar införandet. Lösningar! Litteratur Mentorskap Några krasher i kritiska otestade system Continuous build Konsekvenser Man blir test infected Skriver aldrig för mycket kod Designen går mot IoC och Clean Code Slutsatser och rekommendationer Att lära sig TDD tar ett par månader Inlärningskurvan är knuten till testramverken Svårt att alltid behålla disciplinen Man stoppas av legacykod Testa! Man lär sig testramverken Man lär sig refactoring Man designar kod bättre Att fuska med TDD innebär att vi landar på utvecklardriven testning. 11