Testdriven utveckling
Januari 2009, KTH
Alexander Tarnowski
© Acando AB
Passion for improvements
Teorin bakom testdriven utveckling
© Acando AB
Passion for improvements
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)
© Acando AB
Passion for improvements
1
Testdriven utveckling och XP
Värderingar
Praktiker
Kommunikation
Enkelhet
Feedback
Mod
Respekt
Principer
© Acando AB
Sitta tillsammans
Teamet
Informativ arbetsplats
”Energized work”
Parprogrammering
Stories
Veckocykeln
Kvartalscykeln
Slack
Bygga på 10 minuter
Continuous integration
Testa först
Inkrementell design
Passion for improvements
TDD strävar efter: ”Clean code that works”
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
© Acando AB
Ron Jeffries
Passion for improvements
Förutsägbarhet
© Acando AB
Passion for improvements
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
© Acando AB
Passion for improvements
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>
© Acando AB
Passion for improvements
Metodologin
Skriv ett test som misslyckas
Få testet att köra
Ta bort duplicering
© Acando AB
Passion for improvements
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
© Acando AB
Passion for improvements
Vad är ett test
Testa = utvärdera
Vilken logik testar vi?
Vilka testdata väljer vi?
Bäst att upprepa: tester ska vara oberoende!
© Acando AB
Passion for improvements
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”
© Acando AB
Passion for improvements
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));
© Acando AB
Passion for improvements
Testtekniker
Vi tar oss från rött till grönt genom att:
Fejka
Uppenbara implementationen
Triangulering
© Acando AB
Passion for improvements
Teknik 1: Fejka
TestCalculator.java
assertEquals(4, calculator.plus(3,1));
Calculator.java
int plus(int augend, int addend) {
return 4;
}
© Acando AB
Passion for improvements
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;
}
© Acando AB
Passion for improvements
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 ???;
}
© Acando AB
Passion for improvements
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
© Acando AB
Passion for improvements
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)
© Acando AB
Passion for improvements
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
© Acando AB
Passion for improvements
Svårt att driva med tester
Säkerhetsprogramvara
Parallella system
Realtidsprogramvara
© Acando AB
Passion for improvements
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!
© Acando AB
Passion for improvements
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”
© Acando AB
Passion for improvements
Bortom TDD
Behavior-driven design
Acceptanstestdriven utveckling
© Acando AB
Passion for improvements
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
© Acando AB
Passion for improvements
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
© Acando AB
Passion for improvements
Att införa TDD: de största problemen
Nytt arbetssätt
Inlärningskurvorna för teknikerna och verktygen
Legacykod
Man ser inte nyttan omedelbart
© Acando AB
Passion for improvements
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”
Passion for improvements
© Acando AB
En möjlig inlärningskurva
produktivitet
?
Valfritt ramverk
Databastestning/webbtestning
Refactoring
Testning av kollaboratörer
tid
Teori
JUnit
© Acando AB
Passion for improvements
Problem med legacykod
Inga eller få tester
Affärslogik i databasen
Felimplemterade ”J2EE patterns”
Singletons
M m, m m
Refactoring!
© Acando AB
Passion for improvements
10
Hur vi underlättar införandet. Lösningar!
Litteratur
Mentorskap
Några krasher i kritiska otestade system
Continuous build
© Acando AB
Passion for improvements
Konsekvenser
Man blir ”test infected”
Skriver aldrig för mycket kod
Designen går mot IoC och ”Clean Code”
© Acando AB
Passion for improvements
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.
© Acando AB
Passion for improvements
11