Nybegynners Guide til Testdrevet Utvikling

Å teste koden din er irriterende, men virkningen av å ikke gjøre det kan være størrelsesordre mer irriterende! I denne artikkelen bruker vi testdrevet utvikling for å skrive og teste vår kode mer effektivt.


Hva er testdrevet utvikling?

Siden begynnelsen av datatiden har programmører og insekter kjempet for overlegenhet. Det er en uunngåelig forekomst. Selv de største programmørene blir bytte til disse anomaliene. Ingen kode er trygt. Det er derfor vi tester. Programmerere, minst sane, teste koden ved å kjøre den på utviklingsmaskiner for å sikre at den gjør hva den skal.


Sane programmerer som tester sine programmer.
Bilde med lov av http://www.youthedesigner.com
Insane programmerer som ikke tester sine programmer.
Bilde med lov av http://www.internetannoyanceday.com

Testdrevet utvikling er en programmeringsteknikk som krever at du skriver faktisk kode og automatisk testkode samtidig. Dette sikrer at du tester koden din - og lar deg omprøve koden raskt og enkelt, siden det er automatisert.

Hvordan virker det?

Testdrevet utvikling, eller TDD som vi kaller det fra nå av, dreier seg om en kort iterativ utviklingssyklus som går noe slikt:

  1. Før du skriver kode, må du først skrive en automatisk test for koden din. Mens du skriver de automatiserte testene, må du ta hensyn til alle mulige innganger, feil og utdata. På denne måten er tankene dine ikke overskyet av en kode som allerede er skrevet.
  2. Første gang du kjører din automatiserte test, bør testen mislykkes, og indikerer at koden ikke er klar ennå.
  3. Etterpå kan du begynne programmering. Siden det allerede er en automatisert test, så lenge koden feiler det, betyr det at det fortsatt ikke er klart. Koden kan stilles inntil den overgår alle påstandene.
  4. Når koden passerer testen, kan du da begynne å rydde den opp, via refactoring. Så lenge koden fortsatt passerer testen, betyr det at det fortsatt fungerer. Du trenger ikke lenger å bekymre deg for endringer som introduserer nye feil.
  5. Start det hele igjen med en annen metode eller et program.

Den testdrevne utviklingssyklusen
Bilde med hilsen av http://en.wikipedia.org/wiki/Test-driven_development

Flott, men hvordan er dette bedre enn vanlig testing?

Har du noen gang forsøkt hoppet over å teste et program fordi:

  • Du følte det var bortkastet tid å teste, siden det var bare en liten kodeendring?
  • Du følte lat å teste alt igjen?
  • Du hadde ikke nok tid til å teste fordi prosjektlederen ville ha det flyttet opp til produksjonen ASAP?
  • Du fortalte deg selv at du ville gjøre det "i morgen"?
  • Du måtte velge mellom manuell testing, eller se på den siste episoden av favoritt TV-showet ditt (Big Bang Theory)?

Mesteparten av tiden skjer ingenting, og du flyttes koden din til produksjon uten problemer. Men noen ganger, etter at du har flyttet til produksjon, går alt galt. Du sitter fast ved å fikse hundre hull i et synkende skip, med flere som vises hvert minutt. Du gjør ikke ønsker å finne deg selv i denne situasjonen.


Skru det, bare flytt det til produksjon!
Bilde med hilsen av http://phenomenaonbreak.wordpress.com

TDD var ment å eliminere våre unnskyldninger. Når et program er utviklet ved hjelp av TDD, lar det oss gjøre endringer og teste raskt og effektivt. Alt vi trenger å gjøre er å kjøre automatiserte tester, og voila! Hvis det passerer alle automatiserte tester, så er det bra å gå - hvis ikke, så betyr det bare at vi har slått noe med endringene. Ved å vite hvilke eksakte deler av testen som ble feilet, gjør det oss også lett å finne ut hvilken del av endringene den brøt, så det gjør det enklere å fikse bugsene.


Jeg er solgt. Hvordan gjør vi dette?

Det er en rekke PHP-automatiserte testrammer der ute som vi kan bruke. En av de mest brukte testrammene er PHPUnit.

PHPUnit er et flott testing rammeverk, som lett kan integreres i dine egne prosjekter, eller andre prosjekter bygget på toppen av populære PHP rammer.

For våre formål skjønt, trenger vi ikke det mangfold av funksjoner som PHPUnit tilbyr. I stedet velger vi å lage testene våre ved hjelp av et enklere testramme, kalt SimpleTest.

I de neste trinnene, la oss anta at vi utvikler en gjestebokprogram hvor noen brukere kan legge til og vise gjestebokoppføringer. La oss anta at markeringen er fullført, og at vi ganske enkelt lager en klasse som inneholder applikasjonslogikk i gjesteboken, som er der applikasjonen legger inn og leser til databasen. Leseplassen i denne klassen er hva vi skal utvikle og teste.


Trinn 1. Konfigurer SimpleTest

Dette er uten tvil det enkleste trinnet for alle. Selv denne fyren kunne gjøre det:


Jeg kan gjøre dette? Jeg kan bruke, min, um? hjerne!
Bilde med lov av http://longstreet.typepad.com/

Last ned SimpleTest her, og trekk ut i en mappe av ditt valg - helst mappen der du skal utvikle koden din, eller din PHP include_path for enkel tilgang.

For denne opplæringen har jeg satt opp mappen slik:

Index.php vil kjøre guestbook.php, og påkalle visningsmetoden og vise oppføringene. Inne i klassen mappen er hvor vi skal sette guestbook.php-klassen, og testmappen er der vi plasserer det enkleste biblioteket.


Trinn 2. Planlegg ditt angrep


Bilde med lov av http://connections.smsd.org/veterans

Det andre trinnet, som faktisk er det viktigste, er å begynne å lage tester. For dette trenger du virkelig å planlegge og tenke på hva din funksjon vil gjøre, hvilke mulige innganger det vil få, og de tilsvarende utgangene det vil sende. Dette trinnet ligner å spille et sjakkspill - du må vite alt om motstanderen din (programmet), inkludert alle svakhetene (mulige feil) og styrker (hva skjer hvis det går bra).

Så for vår gjestebok søknad, la oss legge ned skjemaene:

Utsikt

  • Denne funksjonen har ingen innganger, siden den bare henter alle oppføringene fra databasen og sender tilbake dataene som skal skrives ut.
  • Det vil returnere en rekke gjesteboksposter, med navnet på plakaten og hans melding. Hvis det ikke er noen poster, bør det fortsatt returnere en tom rekkefølge.
  • Hvis det er poster, vil arrayet ha 1 eller flere verdier i den.
  • Samtidig vil arrayet ha en spesifikk struktur, noe som:
 Array (['name'] = "Bob" ['message'] = "Hei, jeg er Bob.") [1] => Array ['message'] = "Hei, jeg er Tom."))

Trinn 3. Skriv en test!


Bilde med lov av http://cflhomeless.wordpress.com

Nå kan vi skrive vår første test. La oss begynne med å opprette en fil som heter guestbook_test.php inne i testmappen.

  

La oss da konvertere det vi har bestemt fra trinn to,.

 legg til ("Bob", "Hei, jeg er Bob."); $ gjestebok-> legg til ("Tom", "Hei, jeg er Tom."); $ oppføringer = $ gjestebok-> viewAll (); $ count_is_greater_than_zero = (count ($ entries)> 0); $ Dette-> assertTrue ($ count_is_greater_than_zero); $ this-> assertIsA ($ entries, 'array'); foreach ($ oppføringer som $ entry) $ this-> assertIsA ($ entry, 'array'); $ Dette-> assertTrue (isset ($ entry [ 'name'])); $ Dette-> assertTrue (isset ($ oppføring [ 'beskjed']));  funksjon testViewGuestbookWithNoEntries () $ guestbook = ny gjestebok (); $ Guestbook-> Slett (); // Slett alle oppføringene først slik at vi vet at det er et tomt bord $ entries = $ guestbook-> viewAll (); $ this-> assertEqual ($ entries, array ()); 

Påstandene sørger for at en viss ting er hva det skal være - i utgangspunktet sikrer det at det som returneres er det du forventer at det kommer tilbake. For eksempel, hvis en funksjon skal returnere sann hvis den lykkes, så i vår test, bør vi hevde at returverdi er lik sant.

Som du kan se her, tester vi visning av gjesteboken med oppføringer og uten. Vi kontrollerer om disse to scenariene overholder kriteriene fra trinn to. Du har sikkert også lagt merke til at hver av våre testfunksjoner starter med ordet "test". Vi gjorde dette fordi, når SimpleTest kjører denne klassen, vil den se etter alle funksjonene som starter med ordet "test" og kjøre det.

I vår testklasse har vi også brukt noen påstandsmetoder, for eksempel assertTrue, assertIsA og assertEquals. AssertTrue-funksjonen kontrollerer om en verdi er sann eller ikke. AssertIsA kontrollerer om en variabel er av en bestemt type eller klasse. Og til slutt kontrollerer assertEquals om en variabel er helt lik en viss verdi.

Det finnes andre påstandsmetoder levert av SimpleTest, som er:

assertTrue ($ x) Feil hvis $ x er feil
assertFalse ($ x) Feil hvis $ x er sant
assertNull ($ x) Feil hvis $ x er satt
assertNotNull ($ x) Feil hvis $ x ikke er angitt
assertIsA ($ x, $ t) Feil hvis $ x ikke er klassen eller skriv $ t
assertNotA ($ x, $ t) Feil hvis $ x er av klassen eller skriv inn $ t
assertEqual ($ x, $ y) Feil hvis $ x == $ y er feil
assertNotEqual ($ x, $ y) Feil hvis $ x == $ y er sant
assertWithinMargin ($ x, $ y, $ m) Feil hvis abs ($ x - $ y) < $m is false
assertOutsideMargin ($ x, $ y, $ m) Feil hvis abs ($ x - $ y) < $m is true
assertIdentical ($ x, $ y) Feil hvis $ x == $ y er feil eller en type feilmatching
assertNotIdentical ($ x, $ y) Feil hvis $ x == $ y er sant og typer matcher
assertReference ($ x, $ y) Feil hvis ikke $ x og $ y er den samme variabelen
assertClone ($ x, $ y) Feil hvis ikke $ x og $ y er identiske kopier
assertPattern ($ p, $ x) Feil hvis ikke regex $ p matcher $ x
assertNoPattern ($ p, $ x) Feil hvis regex $ p samsvarer med $ x
expectError ($ x) Svelger eventuelle kommende samsvarsfeil
hevde ($ e) Feil på mislykket forventningsobjekt $ e

Assertion method list med lov av http://www.simpletest.org/en/unit_test_documentation.html


Trinn 4. Unnlater å vinne


Bilde med hilsen av http://verydemotivational.com

Når du er ferdig med å skrive koden, bør du kjøre testen. Første gang du kjører testen, er det SKAL FØLGE. Hvis det ikke gjør det, betyr det at testen din ikke tester noe.

For å kjøre testen, bare kjøre guestbook_test.php i nettleseren din. Du bør se dette først:

Dette skjedde fordi vi ikke har opprettet vår gjesteboksklasse ennå. For å gjøre det, opprett guestbook.php i klassen din. Klassen skal inneholde metodene vi planlegger å bruke, men bør ikke inneholde noe ennå først. Husk at vi skriver tester først før skriver noen kode.

  

Når du kjører testen igjen, bør den se slik ut:

Som vi kan se her, vinner vår test nå ved å feile. Dette betyr at testen vår nå er klar til å bli "besvart".


Bilde med hilsen av http://www.gamercastnetwork.com/forums

Trinn 5. Svar på testen ved å skrive kode


På et tidspunkt har vi alle følt oss slik når vi programmerer.
Bilde med lov av http://fermentation.typepad.com/fermentation

Nå som vi har en fungerende automatisert test, kan vi begynne å skrive kode. Åpne opp din guestbook.php klasse og begynn å skape svaret på testen din.

  'Kirk', 'message' => 'Hei, jeg er Kirk.' ), array ('name' => 'Ted', 'message' => 'Hei, jeg er Ted.')); offentlig funksjon viewAll () // Her bør vi hente alle poster fra databasen. // Dette simuleres ved å returnere $ _entries array Return Self: $ _ entries;  offentlig funksjon legg til ($ navn, $ melding) // Her simulerer vi innføring i databasen ved å legge til en ny post i $ _entries-oppsettet // Dette er den riktige måten å gjøre det på: selv :: $ _ oppføringer [] = array ('name' => $ navn, 'message' => $ melding); selv: $ _ oppføringer [] = array ('notname' => $ navn, 'notmessage' => $ melding); // oops, det er en feil her et sted å returnere sant;  offentlig funksjon deleteAll () // Vi har bare satt $ _entries array for å simulere selv: $ _ entries = array (); returnere sant; 

Denne guestbook.php-klassen har noen feil i det med vilje, så vi kan se hvordan det ser ut hvis vår test mislykkes.

Når vi kjører testen, bør vi se noe slikt:

Testutgangen viser oss i hvilken test og i hvilken påstand vår kode mislyktes. Fra dette kan vi enkelt finne ut at linjen 16 og 17 var påstanden som kastet feilen.

 assertTrue (isset ($ entry [ 'name'])); $ Dette-> assertTrue (isset ($ oppføring [ 'beskjed'])) ;? 

Dette forteller oss tydelig at den returnerte oppføringsarmen ikke hadde den riktige oppsettnøkkelen. Basert på dette, vet vi lett hvilken del av koden vi gikk galt.

  $ navn, 'melding' => $ melding); // fast! returnere sant; ? 

Nå, når vi kjører testen vår igjen, bør den vise oss:


Trinn 6. Refactor og Forbedre koden din


Bilder med lov av http://www.osborneink.com og http://phuketnews.phuketindex.com

Siden koden vi tester her, er det ganske enkelt, ikke vår test- og bugfixering var lang. Men hvis dette var en mer komplisert applikasjon, må du gjøre flere endringer i koden din, gjør den renere, så det er lettere å vedlikeholde, og mange andre ting. Problemet med dette er imidlertid at forandringen vanligvis introduserer flere feil. Det er her vår automatiserte test kommer inn - når vi gjør endringer, kan vi bare kjøre testen igjen. Hvis det fortsatt går, betyr det at vi ikke brøt noe. Hvis det feiler, vet vi at vi har gjort en feil. Det informerer oss også hvor problemet er, og forhåpentligvis hvordan vi kan fikse det.


Trinn 7. Skyll og gjenta


Bilde med lov av http://www.philstockworld.com

Til slutt, når programmet krever ny funksjonalitet, må du skrive nye tester. Det er enkelt! Skyll og gjenta prosedyrene fra trinn to (siden SimpleTest-filene dine allerede skal settes opp), og start syklusen igjen.


Konklusjon

Det er mange dypere testdrevne utviklingsartikler der ute, og enda mer funksjonalitet til SimpleTest enn det som ble vist i denne artikkelen-ting som mock objekter, stubber, som gjør det enklere å lage tester. Hvis du vil lese mer, bør Wikipedias testdrevne utviklingsside sette deg på riktig vei. Hvis du er ivrig etter å bruke SimpleTest som testramme, kan du bla gjennom online dokumentasjonen og være sikker på å se gjennom de andre funksjonene.

Testing er en integrert del av utviklingssyklusen, men det er for ofte det første som skal kuttes når frister er nært forestående. Forhåpentligvis, etter å ha lest denne artikkelen, vil du sette pris på hvor nyttig det er å investere i testdrevet utvikling.

Hva er tankene dine om testdrevet utvikling? Er det noe du er interessert i å implementere, eller tror du det er sløsing med tid? Gi meg beskjed i kommentarene!