Testa din kod är irriterande, men effekten av att inte göra det kan vara storleksordningar mer irriterande! I den här artikeln använder vi testdriven utveckling för att skriva och testa vår kod mer effektivt.
Sedan datortiden började, har programmerare och buggar kämpat för överlägsenhet. Det är en oundviklig händelse. Även de största programmerarna faller byte mot dessa anomalier. Ingen kod är säker. Det är därför som vi testar. Programmerare, åtminstone sinniga, testa deras kod genom att köra den på utvecklingsmaskiner för att se till att det gör vad den ska.
Testdriven utveckling är en programmeringsteknik som kräver att du skriver samma kod och automatisk testkod samtidigt. Detta säkerställer att du testar din kod - och gör att du kan retestera din kod snabbt och enkelt, eftersom det är automatiserat.
Testdriven utveckling, eller TDD som vi kallar det från och med nu, kretsar kring en kort iterativ utvecklingscykel som går något liknande:
Har du någonsin försiktigt hoppat över att testa ett program eftersom:
För det mesta sker inget, och du flyttar din kod till produktion utan problem. Men ibland, efter att du har flyttat till produktion, går allt fel. Du sitter fast och fixar hundra hål i ett sjunkande fartyg, med mer som visas varje minut. Du gör inte vill hitta dig själv i den här situationen.
TDD var tänkt att eliminera våra ursäkter. När ett program har utvecklats med hjälp av TDD, tillåter det oss att göra förändringar och test snabbt och effektivt. Allt vi behöver göra är att köra automatiserade tester, och voila! Om det passerar alla automatiska tester, då är det bra att gå - om inte, så betyder det bara att vi bröt något med förändringarna. Genom att veta vilka exakta delar av testet som misslyckades, gör det också oss lätt att identifiera vilken del av förändringarna den bröt, så det gör det lättare att fixa buggarna.
Det finns en mängd PHP-automatiserade testramar där ute som vi kan använda. En av de mest använda testramarna är PHPUnit.
PHPUnit är ett bra testramverk som enkelt kan integreras i dina egna projekt eller andra projekt som byggs utöver populära PHP-ramar.
För våra ändamål men vi behöver inte de många funktioner som PHPUnit erbjuder. Istället väljer vi att skapa våra test med ett mycket enklare testramverk, kallat SimpleTest.
I nästa steg antar vi att vi utvecklar en gästboksapplikation där någon användare kan lägga till och visa gästbokposter. Låt oss anta att markeringen har slutförts, och att vi helt enkelt gör en klass som innehåller applikationslogik i gästboken, vilket är var applikationen sätter in och läser till databasen. Läsningsdelen av den här klassen är vad vi ska utveckla och testa.
Detta är förmodligen det enklaste steget av alla. Även den här killen kan göra det:
Ladda ner SimpleTest här och extrahera till en mapp efter eget val - helst mappen där du ska utveckla din kod, eller din PHP include_path för enkel åtkomst.
För den här handledningen har jag satt upp mappen så här:
Index.php kör guestbook.php och anropa visningsmetoden och visa inmatningarna. Inne i klassens mapp är där vi lägger guestbook.php-klassen, och testmappen är där vi placerar det enklaste biblioteket.
Det andra steget, som faktiskt är det viktigaste, är att börja skapa dina test. För detta behöver du verkligen planera och tänka på vad din funktion kommer att göra, vilka möjliga ingångar det kommer att få och motsvarande utdata som den ska skicka. Detta steg liknar att spela ett schackspel - du behöver veta allt om din motståndare (programmet), inklusive alla hans svagheter (möjliga fel) och styrkor (vad händer om det går framgångsrikt).
Så för vår gästbok ansökan, låt oss lägga ner schematerna:
Array (['name'] = "Bob" ['message'] = "Hej, jag är Bob.") [1] => Array ['message'] = "Hej, jag är Tom."))
Nu kan vi skriva vårt första test. Låt oss börja med att skapa en fil som heter guestbook_test.php inuti testmappen.
Låt oss då konvertera vad vi har bestämt från steg två,.
lägg till ("Bob", "Hej, jag är Bob."); $ gästbok-> lägg till ("Tom", "Hej, jag är Tom."); $ entries = $ gästbok-> visaAll (); $ count_is_greater_than_zero = (räkna ($ poster)> 0); $ This-> assertTrue ($ count_is_greater_than_zero); $ this-> assertIsA ($ entries, 'array'); foreach ($ poster som $ entry) $ this-> assertIsA ($ entry, 'array'); $ This-> assertTrue (isset ($ inträde [ 'name'])); $ This-> assertTrue (isset ($ inträde [ 'meddelande'])); funktion testViewGuestbookWithNoEntries () $ guestbook = ny gästbok (); $ Guestbook-> TaBortAlla (); // Ta bort alla poster först så vi vet att det är en tom tabell $ entries = $ gästbok-> visaAll (); $ this-> assertEqual ($ entries, array ());Påståenden försäkra sig om att en viss sak är vad den ska vara - i grunden säkerställer den att det som returneras är vad du förväntar dig att återvända. Till exempel, om en funktion är tänkt att återvända sant om det lyckas, då i vårt test, borde vi hävda att returvärdet är lika med sant.
Som du kan se här, testa vi visning av gästboken med poster och utan. Vi kontrollerar om dessa två scenarier passerar våra kriterier från steg två. Du har säkert märkt att alla våra testfunktioner börjar med ordet "test". Det gjorde vi för, när SimpleTest kör denna klass, kommer den att leta efter alla funktioner som börjar med ordet "test" och köra det.
I vår testklass har vi också använt vissa påståenden, såsom assertTrue, assertIsA och assertEquals. AssertTrue-funktionen kontrollerar huruvida ett värde är sant. AssertIsA kontrollerar om en variabel är av en viss typ eller klass. Och slutligen kontrollerar assertEquals om en variabel är helt lika med ett visst värde.
Det finns andra påståenden som tillhandahålls av SimpleTest, som är:
assertTrue ($ x) | Misslyckas om $ x är falskt |
assertFalse ($ x) | Misslyckas om $ x är sant |
assertNull ($ x) | Misslyckas om $ x är inställd |
assertNotNull ($ x) | Misslyckas om $ x inte anges |
assertIsA ($ x, $ t) | Misslyckas om $ x inte är klassen eller skriv $ t |
assertNotA ($ x, $ t) | Misslyckas om $ x är av klassen eller skriv $ t |
assertEqual ($ x, $ y) | Misslyckas om $ x == $ y är falskt |
assertNotEqual ($ x, $ y) | Misslyckas om $ x == $ y är sant |
assertWithinMargin ($ x, $ y, $ m) | Misslyckas om abs ($ x - $ y) < $m is false |
assertOutsideMargin ($ x, $ y, $ m) | Misslyckas om abs ($ x - $ y) < $m is true |
assertIdentical ($ x, $ y) | Misslyckas om $ x == $ y är falsk eller en typmatchning |
assertNotIdentical ($ x, $ y) | Misslyckas om $ x == $ y är sant och typer matchar |
assertReference ($ x, $ y) | Misslyckas, såvida inte $ x och $ y är samma variabel |
assertClone ($ x, $ y) | Misslyckas, såvida inte $ x och $ y är identiska kopior |
assertPattern ($ p, $ x) | Misslyckas om inte regex $ p matchar $ x |
assertNoPattern ($ p, $ x) | Misslyckas om regex $ p matchar $ x |
expectError ($ x) | Svalar något kommande matchningsfel |
hävda ($ e) | Misslyckas på misslyckat förväntat objekt $ e |
Uppgiftsmetodlista med tillstånd av http://www.simpletest.org/en/unit_test_documentation.html
När du är klar med att skriva koden ska du köra testet. Första gången du kör testet, det Borde misslyckas. Om det inte gör det betyder det att ditt test inte testar någonting.
För att köra ditt test, kör helt enkelt guestbook_test.php i din webbläsare. Du bör se detta först:
Detta hände eftersom vi ännu inte har skapat vår gästboksklass. För att göra så skapar du gästboken.php i din klasskatalog. Klassen ska innehålla de metoder vi planerar att använda, men bör inte innehålla någonting än i början. Kom ihåg att vi skriver först testerna innan skriver någon kod.
När du kör testet igen ska det se ut så här:
Som vi kan se här, vinner vårt test nu genom att misslyckas. Det betyder att vårt test nu är klart för att få "svarat".
Bild med tillstånd av http://www.gamercastnetwork.com/forums
Steg 5. Besvara ditt test med skrivkoden
Vid något tillfälle har vi alla känt så här när vi programmerar.
Bild med tillstånd av http://fermentation.typepad.com/fermentationNu när vi har ett fungerande automatiserat test kan vi börja skriva kod. Öppna din guestbook.php klass och börja skapa svaret på ditt test.
'Kirk', 'message' => 'Hej, jag är Kirk.' ), array ('name' => 'Ted', 'message' => 'Hej, jag är Ted.')); public function viewAll () // Här ska vi hämta alla poster från databasen. // Detta simuleras genom att returnera $ _entries array Return Return self: $ _ entries; public function add ($ namn, $ message) // Här simulerar vi infogning i databasen genom att lägga till en ny post i $ _entries array // Detta är rätt sätt att göra det: själv :: $ _ poster [] = array ('name' => $ namn, 'message' => $ meddelande); själv :: $ _ poster [] = array ('notname' => $ namn, 'notmessage' => $ meddelande); // oops, det finns en bugg här någonstans återvänd sant; public function deleteAll () // Vi har bara satt $ _entries array för att simulera själv :: $ _ entries = array (); återvänd sant;Denna gästbok.php-klass har några fel i det med syfte, så vi kan se hur det ser ut om vårt test misslyckas.
När vi kör vårt test bör vi se något så här:
Testutgången visar oss i vilket test och i vilket påstående vår kod misslyckades. Härav kan vi enkelt fastställa att linje 16 och 17 var påståendet som kastade felet.
assertTrue (isset ($ posten [ 'name'])); $ This-> assertTrue (isset ($ posten [ 'meddelande'])) ;?Det här förklarar tydligt att den returnerade postrutan inte hade rätt matrisnyckel. Baserat på detta vet vi lätt vilken del av vår kod som gick fel.
$ namn, "meddelande" => $ meddelande); //fast! återvänd sant; ?Nu när vi kör vårt test igen bör det visa oss:
Steg 6. Refactor och förfina din kod
Bilder med tillstånd av http://www.osborneink.com och http://phuketnews.phuketindex.comEftersom koden vi testar här är ganska enkel har vår testning och buggfixering inte varit så lång. Men om det här var en mer komplex applikation, skulle du behöva göra flera ändringar i din kod, göra den renare så att det är lättare att underhålla, och många andra saker. Problemet med detta är dock att förändringen vanligtvis introducerar ytterligare fel. Det här är vårt automatiserade test som kommer in - när vi gör ändringar, kan vi helt enkelt springa provet igen. Om det fortfarande passerar betyder det att vi inte bryter något. Om det misslyckas vet vi att vi gjorde ett misstag. Den informerar oss också om var problemet är och förhoppningsvis hur vi kan fixa det.
Steg 7. Skölj och repetera
Bild med tillstånd av http://www.philstockworld.comSå småningom, när ditt program kräver ny funktionalitet måste du skriva nya test. Det är lätt! Skölj och upprepa procedurerna från steg två (eftersom dina SimpleTest-filer redan ska ställas in) och starta cykeln igen.
Slutsats
Det finns mycket mer djupgående testdrivna utvecklingsartiklar där ute, och ännu mer funktionalitet än SimpleTest än vad som visades i denna artikel-saker som mocka objekt, stubbar, vilket gör det enklare att skapa test. Om du vill läsa mer, bör Wikipedias testdrivna utvecklingssida sätta dig på rätt väg. Om du är angelägen om att använda SimpleTest som ditt testramverk, bläddra i dokumentationen online och var noga med att granska dess andra funktioner.
Testning är en integrerad del av utvecklingscykeln, men det är för ofta det första som ska skäras när deadlines är nära förestående. Förhoppningsvis kommer du, efter att ha läst den här artikeln, uppskattat hur bra det är att investera i testdriven utveckling.
Vad är dina tankar om testdriven utveckling? Är det något du är intresserad av att genomföra, eller tycker du att det är slöseri med tid? Låt mig veta i kommentarerna!