Du vet det; Jag vet det. Vi borde testa vår kod mer än vi gör. En del av anledningen till att vi inte tror, är att vi inte vet exakt hur. Tja, jag blir av med den ursäkten idag: Jag lär dig att testa ditt PHP med EnhancePHP-ramen.
Jag kommer inte att försöka övertyga dig om att testa din kod; och vi kommer inte att diskutera testdriven utveckling heller. Det har gjorts tidigare på Nettuts +. I den artikeln förklarar Nikko Bautista exakt varför testning är en bra sak och skisserar ett TDD-arbetsflöde. Läs det någon gång, om du inte är bekant med TDD. Han använder också SimpleTest-biblioteket för sina exempel, så om du inte gillar utseendet på EnhancePHP kan du prova SimpleTest som ett alternativ.
Som jag sa använder vi EnhancePHP. Det är ett bra litet PHP-bibliotek - en enda fil - som erbjuder mycket testfunktionalitet.
Börja med att gå över till deras hämtningssida och ta den senaste versionen av ramverket.
Vi ska bygga en riktigt enkel valideringsklass för att testa. Det kommer inte göra för mycket: bara återvända Sann
om objektet passerar validering, eller falsk
om det inte gör det. Så sätt upp ett riktigt enkelt litet projekt:
Vi gör det här är ett semi-TDD-sätt, så låt oss börja med att skriva några tester.
Out lilla klassen kommer att validera tre saker: e-postadresser, användarnamn och telefonnummer.
Men innan vi kommer att skriva aktuella tester måste vi konfigurera vår klass:
val = ny validering ();
Detta är vår start; observera att vi utökar klassen \ Förbättra \ TestFixture
. Genom att göra så låt vi EnhancePHP veta att alla offentliga metoder i den här klassen är tester, med undantag för metoder inrätta
och riva ner
. Som du kan gissa, kör dessa metoder före och efter alla dina test (inte före och efter varje). I det här fallet vårt inrätta
Metoden kommer att skapa en ny Godkännande
instans och tilldela den till en egendom i vår förekomst.
Förresten, om du är relativt ny för PHP, kanske du inte är bekant med det \ Förbättra \ TestFixture
syntax: vad är det med snedstreck? Det är PHP namespacing för dig; kolla in dokumenten om du inte är bekant med den.
Så, testerna!
Låt oss börja med att validera e-postadresser. Som du ser är det bara ganska enkelt att göra ett grundläggande test:
allmän funktion validates_a_good_email_address () $ result = $ this-> val-> validate_email ("[email protected]"); \ Förbättra \ Assert :: isTrue ($ result);
Vi ringer helt enkelt metoden vi vill testa, skickar den till en giltig e-postadress och lagrar $ result
. Sedan handlar vi $ result
till är sant
metod. Den metoden hör till \ Förbättra \ Assert
klass.
Vi vill se till att vår klass avvisar icke-e-postadresser. Så, låt oss testa för det:
offentlig funktion reject_bad_email_addresses () $ val_wrapper = \ Förbättra \ Core :: getCodeCoverageWrapper ('Validation'); $ val_email = $ this-> get_scenario ('validate_email'); $ addresses = array ("john", "[email protected]", "john @ doe.", "jo*[email protected]"); foreach ($ adresser som $ addr) $ val_email-> med ($ addr) -> förvänta (false); $ val_email-> verifyExpectations ();
Detta introducerar en ganska cool funktion av EnhancePHP: scenarier. Vi vill testa en massa icke-e-postadresser för att se till att vår metod kommer tillbaka falsk
. Genom att skapa ett scenario vässar vi väsentligen en förekomst av vår klass i någon EnhancePHP-godhet, skriv mycket mindre kod för att testa alla våra icke-adresser. Det är vad $ val_wrapper
är: en modifierad förekomst av vår Godkännande
klass. Sedan, $ val_email
är scenariobjektet, något som en genväg till validera email
metod.
Sedan har vi en rad strängar som inte bör validera som e-postadresser. Vi slår över den här matrisen med a för varje
slinga. Lägg märke till hur vi kör testet: vi kallar med
metod på vårt scenariobjekt, ger det parametrarna för metoden vi testar. Då kallar vi förvänta
metod på det, och skicka det oavsett vad vi förväntar oss att komma tillbaka.
Slutligen kallar vi scenariot verifyExpectations
metod.
Så, de första testerna är skrivna; hur kör vi dem?
Innan vi kör testen måste vi skapa vår Godkännande
klass. Inuti lib.validation.php
, börja med detta:
Nu inne
test.php
, vi tar allt tillsammans:Först kräver vi alla nödvändiga filer. Då kallar vi
runTests
metod, som hittar våra test.Nästa kommer den snygga delen. Avfyra en server, och du får lite HTML-utskrift:
Mycket trevligt, eller hur? Nu, om du har PHP i din terminal, kör det här i terminalen:
Förbättra PHP märker att du befinner dig i en annan miljö och justerar dess produktion på lämpligt sätt. En fördel med detta är att om du använder en IDE, som PhpStorm, som kan köra enhetsprovningar, kan du se denna terminalutgång direkt inuti IDE.
Du kan också få XML- och TAP-utdata, om det är vad du föredrar, passera bara
\ Förbättra \ TemplateType :: Xml
eller\ Förbättra \ TemplateType :: Tap
tillrunTests
metod för att få rätt produktion. Observera att köra det i terminalen kommer också att producera kommandoradsresultat, oavsett vad du skickar tillrunTests
.Få testen att passera
Låt oss skriva den metod som gör att våra tester passerar. Som du vet är det det
validera email
. På toppen avGodkännande
klass, låt oss definiera en offentlig egendom:offentliga $ email_regex = '/^[\w+-_\.]+@[\w\.]+\.\w+$/';Jag sätter detta i en offentlig egendom så att om användaren vill byta ut den med egen regex kunde de. Jag använder den här enkla versionen av en e-postregex, men du kan ersätta den med din favoritregex om du vill.
Då är det metoden:
allmän funktion validate_email ($ address) returnera preg_match ($ this-> email_regex, $ address) == 1Nu kör vi testen igen och:
Skriva fler test
Tid för fler test:
användarnamn
Låt oss skapa några tester för användarnamn nu. Våra krav är helt enkelt att det måste vara en 4 till 20 teckensträng som bara består av ordtecken eller perioder. Så:
allmän funktion validates_a_good_username () $ result = $ this-> val-> validate_username ("some_user_name.12"); \ Förbättra \ Assert :: isTrue ($ result);Nu, vad sägs om några användarnamn som inte ska validera:
allmän funktion rejects_bad_usernames () $ val_username = $ this-> get_scenario ('validate_username'); $ användarnamn = array ("namn med mellanslag", "nej! utmaning! mark", "ts", "detta användarnamnIsTooLongItShouldBeBetweenFourAndTwentyCharacters"); foreach ($ användarnamn som $ namn) $ val_username-> med ($ name) -> expect (false); $ val_username-> verifyExpectations ();Det här är väldigt lik vårt
reject_bad_email_addresses
fungera. Observera dock att vi ringer dettaget_scenario
metod: var kommer det ifrån? Jag abstraherar scenariseringsfunktionen till privat metod, längst ner i vår klass:privat funktion get_scenario ($ method) $ val_wrapper = \ Förbättra \ Core :: getCodeCoverageWrapper ('Validation'); returnera \ Förbättra \ Core :: getScenario ($ val_wrapper, $ metod);Vi kan använda detta i vår
reject_bad_usernames
och ersätt scenariet skapa ireject_bad_email_addresses
också. Eftersom detta är en privat metod, kommer EnhancePHP inte att försöka köra det som ett normalt test, hur det kommer med offentliga metoder.Vi gör dessa test på samma sätt som hur vi gjorde den första uppsättningen passera:
# Överst ... public $ username_regex = '/^[\w\.]4,20$/'; # och metoden ... public function validate_username ($ användarnamn) returnera preg_match ($ this-> användarnamn_regex, $ användarnamn) == 1;Det här är ganska grundläggande, men det är allt som behövs för att möta vårt mål. Om vi ville återge en förklaring i händelse av fel kan du göra något så här:
allmän funktion validate_username ($ användarnamn) $ len = strlen ($ användarnamn); om ($ len < 4 || $len > 20) return "Användarnamnet måste vara mellan 4 och 20 tecken"; elseif (preg_match ($ this-> användarnamn_regex, $ användarnamn) == 1) return true; else return "Användarnamnet får bara innehålla bokstäver, siffror, understreck eller perioder.";Naturligtvis kan du också kolla om användarnamnet redan finns.
Nu kör testen och du bör se dem alla som passerar.
Telefonnummer
Jag tror att du hänger med det här nu, så låt oss avsluta vårt valideringsexempel genom att kolla telefonnummer:
allmän funktion validates_good_phonenumbers () $ val_phonenumber = $ this-> get_scenario ("validate_phonenumber"); $ nummer = array ("1234567890", "(890) 123-4567", "123-456-7890", "123 456 7890", "(123) 456 7890"); foreach ($ tal som $ num) $ val_phonenumber-> med ($ num) -> expect (true); $ val_phonenumber-> verifyExpectations (); allmän funktion rejects_bad_phonenumnbers () $ result = $ this-> val-> validate_phonenumber ("123456789012"); \ Förbättra \ Assert :: isFalse ($ result);Du kan noga räkna ut
Godkännande
metod:offentlig $ phonenumber_regex = '/ ^ \ d 10 $ | ^ (\ (? \ d 3 \)? [| -] \ d 3 [| -] \ d 4) $ /'; allmän funktion validate_phonenumber ($ number) returnera preg_match ($ this-> phonenumber_regex, $ number) == 1;Nu kan vi köra alla testerna tillsammans. Så här ser det ut som kommandoraden (min föredragna testmiljö):
Övrig testfunktionalitet
Naturligtvis kan EnhancePHP göra mycket mer än vad vi har tittat på i det här lilla exemplet. Låt oss titta på något av det nu.
Vi träffade oss mycket kort
\ Förbättra \ Assert
klass i vårt första test. Vi brukade inte använda det annars, eftersom det inte är användbart när du använder scenarier. Men det är där alla påståenden är. Skönheten hos dem är att deras namn gör deras funktionalitet otroligt uppenbart. Följande testexempel skulle passera:
\ Förbättra \ Assert :: areIdentical ("Nettuts +", "Nettuts +")
\ Förbättra \ Assert :: areNotIdentical ("Nettuts +", "Psdtuts +")
\ Förbättra \ Assert :: isTrue (sant)
\ Förbättra \ Assert :: isFalse (false)
\ Förbättra \ Assert :: innehåller ("Net", "Nettuts +")
\ Förbättra \ Assert :: isNull (null)
\ Förbättra \ Assert :: isNotNull ( 'Nettust +')
\ Förbättra \ Assert :: isInstanceOfType ("Undantag", ny Undantag (""))
\ Förbättra \ Assert :: isNotInstanceOfType ("String", ny Undantag (""))
Det finns också några andra påståenden. Du kan kontrollera dokumenten för en komplett lista och exempel.
EnhancePHP kan också göra mocks och stubbar. Har du inte hört talas om mocks och stubbar? Jo, de är inte för komplicerade. En mock är en omslag för objekt som kan hålla reda på vilka metoder som kallas, med vilka egenskaper de kallas och vilka värden som returneras. En mock kommer att ha lite test för att verifiera, som vi ser.
Här är ett litet exempel på en mock. Låt oss börja med en mycket enkel klass som räknas:
num = $ this-> num + $ num; returnera $ this-> num;
Vi har en funktion: ökning
, som accepterar en parameter (men är standard 1) och ökar $ num
egendom med det numret.
Vi kan använda den här klassen om vi byggde en resultattavla:
klassresultattavla public $ home = 0; offentlig $ away = 0; offentlig funktion __construct ($ home, $ away) $ this-> home_counter = $ home; $ this-> away_counter = $ away; allmän funktion score_home () $ this-> home = $ this-> home_counter-> increment (); returnera $ this-> hem; allmän funktion score_away () $ this-> away = $ this-> away_counter-> increment (); returnera $ this-> hem;
Nu vill vi testa för att se till att Disken
förekomstmetod ökning
fungerar korrekt när Resultattavlan tavlan~~POS=HEADCOMP
Exempel metoder kallar det. Så vi skapar det här testet:
klassen ScoreboardTest utökar \ Enhance \ TestFixture public function score_home_calls_increment () $ home_counter_mock = \ Förbättra \ MockFactory :: createMock ("Counter"); $ away_counter = ny Counter (); $ home_counter_mock-> addExpectation (\ Förbättra \ Expect :: metod ('inkrement')); $ resultattavla = ny resultattavla ($ home_counter_mock, $ away_counter); $ Scoreboard-> score_home (); $ Home_counter_mock-> verifyExpectations (); \ Förbättra \ Core :: runTests ();
Observera att vi börjar med att skapa $ home_counter_mock
: Vi använder EnhancePHP mock fabriken, passerar det namnet på den klass vi mocking. Detta returnerar en "förpackad" instans av Disken
. Sedan lägger vi till en förväntan, med denna linje
$ home_counter_mock-> addExpectation (\ Förbättra \ Expect :: metod ('inkrement'));
Vår förväntan säger bara att vi förväntar oss ökning
metod som ska kallas.
Därefter fortsätter vi att skapa Resultattavlan tavlan~~POS=HEADCOMP
exempel och ringa score_home
. Då vi verifyExpectations
. Om du kör det här ser du att vårt test passerar.
Vi kan också ange vilka parametrar vi vill ha en metod på det spotta objektet som ska ringas med, vilket värde som returneras, eller hur många gånger metoden ska kallas med något sådant:
$ home_counter_mock-> addExpectation (\ Förbättra \ Expect :: metod ('inkrement') -> med (10)); $ home_counter_mock-> addExpectation (\ Förbättra \ Expect :: metod ('inkrement') -> gånger (2)); $ home_counter_mock-> addExpectation (\ Förbättra \ Expect :: metod ('inkrement') -> returnerar (1)); $ home_counter_mock-> addExpectation (\ Förbättra \ Expect :: metod ('inkrement') -> med (3) -> gånger (1)); $ home_counter_mock-> addExpectation (\ Förbättra \ Expect :: metod ('inkrement') -> med (2) -> returnerar (2));
Jag borde nämna det, medan med
och gånger
kommer att visa misslyckade test om förväntningarna inte är avsedda, avkastning
inte. Du måste lagra returvärdet och använda ett påstående till det. Jag är inte säker på varför så är fallet, men varje bibliotek har sina quirks :). (Du kan se ett exempel på detta i bibliotekets exempel i Github.)
Sedan finns det stubbar. En stub fyller in för ett verkligt objekt och en metod, och återvänder precis vad du berättar för det. Så, låt oss säga att vi vill se till att vår Resultattavlan tavlan~~POS=HEADCOMP
exemplet använder rätt värdet det tar emot från ökning
, vi kan stubba a Disken
exempel så att vi kan styra vad ökning
kommer att återvända:
klass ScoreboardTest utökar \ Förbättra \ TestFixture public function score_home_calls_increment () $ home_counter_stub = \ Förbättra \ StubFactory :: createStub ("Counter"); $ away_counter = ny Counter (); $ home_counter_stub-> addExpectation (\ Förbättra \ Expect :: metod ('inkrement') -> returnerar (10)); $ resultattavla = ny resultattavla ($ home_counter_stub, $ away_counter); $ result = $ resultattavla-> score_home (); \ Förbättra \ Assert :: areIdentical ($ resultat, 10); \ Förbättra \ Core :: runTests ();
Här använder vi \ Förbättra \ StubFactory :: createStub
för att skapa vår stubber. Sedan lägger vi till en förväntan om att metoden ökning
kommer tillbaka 10. Vi kan se att resultatet är det vi kan förvänta oss, med tanke på vår kod.
För mer exempel på mocks och stubbar med EnhancePHP-biblioteket, kolla in Github Repo.
Tja, det är en titt på testning i PHP, med hjälp av EnhancePHP-ramen. Det är en oerhört enkel ram, men det ger allt du behöver för att göra en viss enkel testning på din PHP-kod. Även om du väljer en annan metod / ram för att testa din PHP (eller kanske rulla din egen!), Hoppas jag att denna handledning har gett intresse för att testa din kod och hur enkelt det kan vara.
Men kanske du redan testar ditt PHP. Låt oss alla veta vad du använder i kommentarerna; trots allt är vi alla här för att lära av varandra! Tack så mycket för att stanna vid!