Du vet det; Jeg vet det. Vi bør teste koden mer enn vi gjør. En del av grunnen til at vi ikke tror jeg er, at vi ikke vet nøyaktig hvordan. Vel, jeg blir kvitt den unnskyldningen i dag: Jeg lærer deg å teste PHP med EnhancePHP-rammeverket.
Jeg kommer ikke til å prøve å overbevise deg om å teste koden din; og vi skal heller ikke diskutere testdrevet utvikling. Det har blitt gjort før på Nettuts +. I den artikkelen forklarer Nikko Bautista nøyaktig hvorfor testing er en god ting og skisserer en TDD-arbeidsflyt. Les det en gang, hvis du ikke er kjent med TDD. Han bruker også SimpleTest-biblioteket for sine eksempler, så hvis du ikke liker utseendet til EnhancePHP, kan du prøve SimpleTest som et alternativ.
Som sagt, bruker vi EnhancePHP. Det er et flott lite PHP-bibliotek - en enkelt fil - som tilbyr mye testfunksjonalitet.
Start med å gå over til nedlastingssiden og ta tak i den nyeste versjonen av rammen.
Vi skal bygge en veldig enkel valideringsklasse for å teste. Det vil ikke gjøre for mye: bare kom tilbake ekte
hvis elementet passerer validering, eller falsk
hvis det ikke gjør det. Så sett opp et veldig enkelt lite prosjekt:
Vi gjør dette er en semi-TDD-mote, så la oss starte med å skrive noen få tester.
Out liten klasse skal validere tre ting: e-postadresser, brukernavn og telefonnumre.
Men før vi kommer til å skrive faktiske tester, må vi sette opp vår klasse:
val = ny validering ();
Dette er vår start; Legg merke til at vi utvider klassen \ Forbedre \ TestFixture
. Ved å gjøre det, lar vi EnhancePHP vite at noen offentlige metoder i denne klassen er tester, med unntak av metoder Setup
og rive ned
. Som du kan gjette, kjører disse metodene før og etter alle tester (ikke før og etter hver). I dette tilfellet, vår Setup
Metoden vil skape en ny Validering
forekomst og tilordne den til en eiendom på vår forekomst.
Forresten, hvis du er relativt ny til PHP, er du kanskje ikke kjent med det \ Forbedre \ TestFixture
syntaks: hva er det med skråstreker? Det er PHP namespacing for deg; sjekk ut dokumentene hvis du ikke er kjent med det.
Så, testene!
La oss starte med å validere e-postadresser. Som du ser, er det bare å gjøre en grunnleggende test:
offentlig funksjon validates_a_good_email_address () $ result = $ this-> val-> validate_email ("[email protected]"); \ Forbedre \ Assert :: isTrue ($ resultat);
Vi kaller bare metoden vi vil teste, sender den en gyldig e-postadresse og lagrer $ resultat
. Så håndterer vi $ resultat
til er sant
metode. Den metoden tilhører \ Forbedre \ Assert
klasse.
Vi vil sørge for at vår klasse avviser ikke-e-postadresser. Så, la oss teste for det:
offentlig funksjon reject_bad_email_addresses () $ val_wrapper = \ Forbedre \ Core :: getCodeCoverageWrapper ('Validation'); $ val_email = $ this-> get_scenario ('validate_email'); $ adresser = array ("john", "[email protected]", "john @ doe.", "jo*[email protected]"); foreach ($ adresser som $ addr) $ val_email-> med ($ addr) -> forvent (false); $ val_email-> verifyExpectations ();
Dette introduserer en ganske kul funksjon av EnhancePHP: scenarier. Vi vil teste en rekke ikke-e-postadresser for å sikre at metoden vår kommer tilbake falsk
. Ved å lage et scenario, pakker vi i hovedsak en forekomst av klassen vår i noen EnhancePHP-godhet, skriver mye mindre kode for å teste alle våre ikke-adresser. Det er hva $ val_wrapper
er: en modifisert forekomst av vår Validering
klasse. Deretter, $ val_email
er scenario objektet, noe som en snarvei til validate_email
metode.
Deretter har vi en rekke strenger som ikke bør validere som e-postadresser. Vi slår over det arrayet med a for hver
sløyfe. Legg merke til hvordan vi driver testen: vi kaller med
metode på vårt scenario objekt, passerer det parametrene for metoden vi tester. Så kaller vi forvente
metode på det, og gi det det vi forventer å komme tilbake.
Til slutt kalder vi scenariet verifyExpectations
metode.
Så, de første testene er skrevet; hvordan kjører vi dem?
Før vi faktisk driver testene, må vi opprette vår Validering
klasse. Innsiden lib.validation.php
, begynn med dette:
Nå i
test.php
, vi trekker alt sammen:Først må vi kreve alle nødvendige filer. Så kaller vi
runTests
metode, som finner våre tester.Neste kommer den ryddige delen. Brann opp en server, og du får litt fin HTML-utgang:
Veldig fint, ikke sant? Nå, hvis du har PHP i terminalen din, kjører dette i terminalen:
EnhancePHP merker at du er i et annet miljø, og justerer sin utgang på riktig måte. En fordel ved dette er at hvis du bruker en IDE, som PhpStorm, som kan kjøre enhetstester, kan du se denne terminalutgangen rett innenfor IDE.
Du kan også få XML- og TAP-utgang, hvis det er det du foretrekker, bare pass
\ Forbedre \ TemplateType :: Xml
eller\ Forbedre \ TemplateType :: Tap
tilrunTests
metode for å få riktig utgang. Vær oppmerksom på at du kjører det i terminalen, vil også gi kommandolinjeresultat, uansett hva du overfører tilrunTests
.Får prøvene å passere
La oss skrive metoden som fører til at våre tester passerer. Som du vet, det er det
validate_email
. På toppen avValidering
klasse, la oss definere en offentlig eiendom:offentlig $ email_regex = '/^[\w+-_\.]+@[\w\.]+\.\w+$/';Jeg setter dette i en offentlig eiendom slik at hvis brukeren ønsker å erstatte den med sin egen regex, kunne de. Jeg bruker denne enkle versjonen av en e-postregex, men du kan erstatte den med din favorittregex hvis du vil.
Deretter er metoden:
offentlig funksjon validate_email ($ address) return preg_match ($ this-> email_regex, $ address) == 1Nå kjører vi testene igjen, og:
Skrive flere prøver
Tid for flere tester:
brukernavn
La oss lage noen tester for brukernavn nå. Våre krav er ganske enkelt at det må være en 4 til 20 tegnstreng som bare består av ordkarakterer eller perioder. Så:
offentlig funksjon validates_a_good_username () $ result = $ this-> val-> validate_username ("some_user_name.12"); \ Forbedre \ Assert :: isTrue ($ resultat);Nå, hva med noen brukernavn som ikke bør validere:
offentlig funksjon rejects_bad_usernames () $ val_username = $ this-> get_scenario ('validate_username'); $ brukernavn = array ("navn med plass", "nei! utrop! mark", "ts", "dette brukernavnetIsTooLongItShouldBeBetweenFourAndTwentyCharacters"); foreach ($ brukernavn som $ navn) $ val_username-> med ($ navn) -> forvent (false); $ val_username-> verifyExpectations ();Dette ligner veldig vår
reject_bad_email_addresses
funksjon. Legg merke til at vi ringer detteget_scenario
metode: hvor er det som kommer fra? Jeg abstraherer scenarietableringsfunksjonaliteten til privat metode, nederst i vår klasse:privat funksjon get_scenario ($ metode) $ val_wrapper = \ Forbedre \ Core :: getCodeCoverageWrapper ('Validation'); returnere \ Forbedre \ Core :: getScenario ($ val_wrapper, $ metode);Vi kan bruke dette i vår
reject_bad_usernames
og erstatt scenarioopprettelsen ireject_bad_email_addresses
også. Fordi dette er en privat metode, vil EnhancePHP ikke prøve å kjøre den som en vanlig test, slik det vil med offentlige metoder.Vi gjør disse testene på samme måte som hvordan vi gjorde det første settet:
# På toppen ... offentlig $ username_regex = '/^[\w\.]4,20$/'; # og metoden ... offentlig funksjon validate_username ($ brukernavn) return preg_match ($ this-> brukernavn_regex, $ brukernavn) == 1;Dette er ganske grunnleggende, selvfølgelig, men det er alt som trengs for å møte vårt mål. Hvis vi ønsket å returnere en forklaring i tilfelle feil, kan du gjøre noe slikt:
offentlig funksjon validate_username ($ brukernavn) $ len = strlen ($ brukernavn); hvis ($ len < 4 || $len > 20) return "Brukernavn må være mellom 4 og 20 tegn"; elseif (preg_match ($ this-> brukernavn_regex, $ brukernavn) == 1) return true; else return "Brukernavn må bare inneholde bokstaver, tall, understreker eller perioder.";Selvfølgelig kan du også sjekke om brukernavnet allerede eksisterer.
Kjør nå testene, og du bør se dem alle bestått.
Telefonnummer
Jeg tror du får tak i dette nå, så la oss fullføre vårt valideringseksempel ved å sjekke telefonnumre:
offentlig funksjon validates_good_phonenumbers () $ val_phonenumber = $ this-> get_scenario ("validate_phonenumber"); $ tall = array ("1234567890", "(890) 123-4567", "123-456-7890", "123 456 7890", "(123) 456 7890"); foreach ($ tall som $ num) $ val_phonenumber-> med ($ num) -> forvent (true); $ val_phonenumber-> verifyExpectations (); offentlig funksjon rejects_bad_phonenumnbers () $ result = $ this-> val-> validate_phonenumber ("123456789012"); \ Forbedre \ Assert :: isFalse ($ resultat);Du kan sikkert finne ut av
Validering
metode:offentlig $ $ nummer \ re offentlig funksjon validate_phonenumber ($ number) return preg_match ($ this-> phonenumber_regex, $ number) == 1;Nå kan vi kjøre alle tester sammen. Her ser det ut som fra kommandolinjen (mitt foretrukne testmiljø):
Øvrig testfunksjonalitet
Selvfølgelig kan EnhancePHP gjøre mye mer enn det vi har sett på i dette lille eksemplet. La oss se på noe av det nå.
Vi møtte oss veldig kort
\ Forbedre \ Assert
klasse i vår første test. Vi brukte ikke det ellers, fordi det ikke er nyttig når du bruker scenarier. Men det er der alle påstandsmetoder er. Skjønnheten hos dem er at deres navn gjør deres funksjonalitet utrolig åpenbar. Følgende testeksempler ville passere:
\ Forbedre \ Assert :: areIdentical ("Nettuts +", "Nettuts + +")
\ Forbedre \ Assert :: areNotIdentical ("Nettuts +", "Psdtuts +")
\ Forbedre \ Assert :: isTrue (true)
\ Forbedre \ Assert :: isFalse (falsk)
\ Enhance \ Assert :: inneholder ("Net", "Nettuts +")
\ Forbedre \ Assert :: isNull (null)
\ Forbedre \ Assert :: isNotNull ( 'Nettust +')
\ Forbedre \ Assert :: isInstanceOfType ('Unntak', ny Unntak (""))
\ Forbedre \ Assert :: isNotInstanceOfType ('String', ny Unntak (""))
Det er også noen andre påstandsmetoder; Du kan sjekke dokumentene for en komplett liste og eksempler.
EnhancePHP kan også gjøre mocks og stubber. Har ikke hørt om mocks og stubber? Vel, de er ikke så kompliserte. En mock er en wrapper for objekt, som kan holde styr på hvilke metoder som kalles, med hvilke egenskaper de kalles, og hvilke verdier blir returnert. En mock vil ha noen test for å verifisere, som vi ser.
Her er et lite eksempel på en mock. La oss starte med en veldig enkel klasse som teller:
num = $ this-> num + $ num; returnere $ this-> num;
Vi har en funksjon: tilvekst
, som aksepterer en parameter (men som standard 1), og øker $ num
eiendom med det nummeret.
Vi kan bruke denne klassen hvis vi bygde en resultattavle:
klasse resultattavle offentlig $ home = 0; offentlig $ away = 0; offentlig funksjon __construct ($ home, $ away) $ this-> home_counter = $ home; $ this-> away_counter = $ away; offentlig funksjon score_home () $ this-> home = $ this-> home_counter-> increment (); returner $ this-> hjem; offentlig funksjon score_away () $ this-> away = $ this-> away_counter-> increment (); returner $ this-> hjem;
Nå vil vi teste for å sikre at Disk
eksempelmetode tilvekst
Fungerer riktig når Scoreboard
eksempel metoder kalle det. Så vi lager denne testen:
klasse ScoreboardTest utvider \ Enhance \ TestFixture offentlig funksjon score_home_calls_increment () $ home_counter_mock = \ Forbedre \ MockFactory :: createMock ("Counter"); $ away_counter = ny Counter (); $ home_counter_mock-> addExpectation (\ Forbedre \ Expect :: metode ('inkrement')); $ resultattavle = ny resultattavle ($ home_counter_mock, $ away_counter); $ Scoreboard-> score_home (); $ Home_counter_mock-> verifyExpectations (); \ Forbedre \ Core :: runTests ();
Legg merke til at vi begynner med å lage $ home_counter_mock
: Vi bruker EnhancePHP mock fabrikken, passerer den navnet på klassen vi mocking. Dette returnerer en "innpakket" forekomst av Disk
. Deretter legger vi til en forventning, med denne linjen
$ home_counter_mock-> addExpectation (\ Forbedre \ Expect :: metode ('inkrement'));
Vår forventning sier bare at vi forventer tilvekst
metode som skal kalles.
Etter det fortsetter vi å lage Scoreboard
eksempel og ring score_home
. Da vi verifyExpectations
. Hvis du kjører dette, ser du at testen vår passerer.
Vi kan også angi hvilke parametre vi vil ha en metode på den spotte gjenstanden som skal kalles med, hvilken verdi returneres, eller hvor mange ganger metoden skal kalles, med noe slikt:
$ home_counter_mock-> addExpectation (\ Forbedre \ Expect :: metode ('inkrement') -> med (10)); $ home_counter_mock-> addExpectation (\ Forbedre \ Expect :: metode ('inkrement') -> ganger (2)); $ home_counter_mock-> addExpectation (\ Forbedre \ Expect :: metode ('increment') -> returnerer (1)); $ home_counter_mock-> addExpectation (\ Forbedre \ Expect :: metode ('inkrement') -> med (3) -> ganger (1)); $ home_counter_mock-> addExpectation (\ Forbedre \ Expect :: metode ('inkrement') -> med (2) -> returnerer (2));
Jeg bør nevne det, mens med
og ganger
vil vise mislykkede tester hvis forventningene ikke er ment, avkastning
ikke. Du må lagre avkastningsverdien og bruke en påstand om det. Jeg er ikke sikker på hvorfor det er tilfelle, men hvert bibliotek har sine quirks :). (Du kan se et eksempel på dette i bibliotekets eksempler i Github.)
Deretter er det stubber. En stub fylles inn for et ekte objekt og en metode, og returnerer akkurat det du forteller det til. Så, la oss si at vi vil sørge for at vår Scoreboard
forekomsten er riktig å bruke verdien den mottar fra tilvekst
, vi kan stubbe a Disk
eksempel slik at vi kan kontrollere hva tilvekst
vil returnere:
klasse ScoreboardTest utvider \ Enhance \ TestFixture offentlig funksjon score_home_calls_increment () $ home_counter_stub = \ Forbedre \ StubFactory :: createStub ("Counter"); $ away_counter = ny Counter (); $ home_counter_stub-> addExpectation (\ Forbedre \ Expect :: metode ('inkrement') -> returnerer (10)); $ resultattavle = ny resultattavle ($ home_counter_stub, $ away_counter); $ result = $ resultattavle-> score_home (); \ Forbedre \ Assert :: areIdentical ($ resultat, 10); \ Forbedre \ Core :: runTests ();
Her bruker vi \ Forbedre \ StubFactory :: createStub
å skape vår stubber. Deretter legger vi til en forventning om at metoden tilvekst
vil returnere 10. Vi kan se at resultatet er det vi forventer, gitt vår kode.
For flere eksempler på mocks og stub med EnhancePHP-biblioteket, sjekk ut Github Repo.
Vel, det er en titt på testing i PHP, ved hjelp av EnhancePHP-rammen. Det er et utrolig enkelt rammeverk, men det gir alt du trenger for å gjøre noen enkle enhetstesting på PHP-koden din. Selv om du velger en annen metode / ramme for å teste din PHP (eller kanskje rulle din egen!), Håper jeg denne opplæringen har gitt interesse i å teste koden din, og hvor enkelt det kan være.
Men kanskje du allerede tester PHP. La oss alle vite hva du bruker i kommentarene; Tross alt er vi alle her for å lære av hverandre! Takk så mye for å stoppe forbi!