Tu știi asta; Stiu. Ar trebui să ne testeze codul mai mult decât noi. O parte din motivul pentru care nu cred, cred, este că nu știm exact cum. Ei bine, mă scap de acea scuză astăzi: vă învăț să vă testați PHP cu cadrul EnhancePHP.
Nu voi încerca să te conving să-ți testezi codul; și nu vom discuta nici despre Test Driven Development. Asta sa întâmplat până acum pe Nettuts +. În acest articol, Nikko Bautista explică exact de ce testarea este un lucru bun și conturează un flux de lucru TDD. Citiți asta cândva, dacă nu sunteți familiarizat cu TDD. De asemenea, el folosește biblioteca SimpleTest pentru exemplele sale, deci dacă nu vă place aspectul EnhancePHP, puteți încerca SimpleTest ca o alternativă.
Așa cum am spus, vom folosi EnhancePHP. Este o mică bibliotecă PHP - un singur fișier - care oferă o mulțime de funcționalități de testare.
Începeți prin a vă îndrepta către pagina de descărcare și prindeți cea mai recentă versiune a cadrului.
Vom construi o clasă de validare foarte simplă pentru a testa. Nu va face prea mult: întoarce-te Adevărat
dacă elementul trece validarea sau fals
dacă nu. Așadar, organizați un proiect foarte simplu:
Vom face acest lucru este un mod semi-TDD, așa că haideți să începem prin a scrie câteva teste.
O clasă mică va valida trei lucruri: adrese de e-mail, nume de utilizator și numere de telefon.
Dar înainte de a începe să scriem teste reale, va trebui să ne stabilim clasa:
val = Validare nouă ();
Acesta este începutul nostru; observați că extindem clasa \ Consolidarea \ TestFixture
. Procedând astfel, lăsăm EnhancePHP să știe că toate metodele publice din această clasă sunt teste, cu excepția metodelor înființat
și dărâma
. Așa cum ați putea ghici, aceste metode se execută înainte și după toate testele (nu înainte și după fiecare). În acest caz, ale noastre înființat
metoda va crea un nou Validare
instanță și să o atribuim unei proprietăți instanței noastre.
Apropo, dacă sunteți relativ nou la PHP, este posibil să nu fiți familiarizați cu asta \ Consolidarea \ TestFixture
sintaxă: ce-i cu tălpile? Aceasta este numele PHP pentru tine; verificați documentele dacă nu sunteți familiarizați cu acestea.
Deci, testele!
Să începem prin validarea adreselor de e-mail. Așa cum veți vedea, doar a face un test de bază este destul de simplu:
funcția publică validates_a_good_email_address () $ rezultat = $ this-> val-> validate_email ("[email protected]"); \ Consolidarea \ Assert :: isTrue ($ rezultat);
Pur și simplu numim metoda pe care dorim să o testăm, oferindu-i o adresă de e-mail validă și stocându-o $ rezultat
. Apoi, noi mâna $ rezultat
la este adevarat
metodă. Această metodă aparține \ Consolidarea \ Assert
clasă.
Vrem să ne asigurăm că clasa noastră va respinge adrese non-e-mail. Deci, să testați pentru asta:
funcția publică reject_bad_email_addresses () $ val_wrapper = \ Enhance \ Core :: getCodeCoverageWrapper ("Validare"); $ val_email = $ acest-> get_scenario ('validate_email'); $ addresses = array ("john", "[email protected]", "john @ doe", "jo*[email protected]"); foreach ($ adresă ca $ addr) $ val_email-> cu ($ addr) -> așteptați (false); $ val_email-> verifyExpectations ();
Aceasta introduce o caracteristică destul de interesantă a scenariilor EnhancePHP: scenarii. Vrem să testăm o grămadă de adrese non-e-mail pentru a ne asigura că metoda noastră va reveni fals
. Prin crearea unui scenariu, înfășurăm în esență o instanță a clasei noastre într-o bunătate EnhancePHP, scriem un cod mult mai mic pentru a testa toate ne-adresele noastre. Asta e ceea ce $ val_wrapper
este: o instanță modificată a noastră Validare
clasă. Atunci, $ val_email
este obiectul scenariului, oarecum ca o scurtătură către validati emailul
metodă.
Apoi, avem o serie de șiruri care nu ar trebui să fie validate ca adrese de e-mail. Vom bate peste acea matrice cu a pentru fiecare
buclă. Observați cum executăm testul: sunăm cu
pe obiectul scenariului nostru, trecând parametrii pentru metoda pe care o testăm. Apoi, numim aştepta
metodă pe aceasta, și să treacă tot ce ne așteptăm să ne întoarcem.
În cele din urmă, numim scenariul verifyExpectations
metodă.
Deci, sunt scrise primele teste; cum le conducem?
Înainte de a executa testele, va trebui să ne creăm Validare
clasă. Interior lib.validation.php
, începeți cu aceasta:
Acum in
test.php
, o vom trage împreună:În primul rând, vom cere toate fișierele necesare. Apoi, numim
runTests
metoda, care găsește testele noastre.Urmează partea potrivită. Porniți un server și veți obține o ieșire HTML bună:
Foarte frumos, nu? Acum, dacă aveți PHP în terminalul dvs., rulați acest lucru este în terminal:
EnhancePHP avertizează că vă aflați într-un mediu diferit și ajustați corespunzător rezultatele. Un beneficiu lateral al acestui lucru este că dacă utilizați un IDE, cum ar fi PhpStorm, care poate rula unități de testare, puteți vizualiza acest ieșire terminale chiar în interiorul IDE.
De asemenea, puteți obține ieșiri XML și TAP, dacă asta preferați, treceți
\ Consolidarea \ TemplateType :: xml
sau\ Consolidarea \ TemplateType :: Tap
larunTests
pentru obținerea rezultatelor corespunzătoare. Rețineți că rularea în terminal va produce rezultate de linie de comandă, indiferent de ce trecețirunTests
.Pregătirea testelor pentru a trece
Să scriem metoda care face ca încercările noastre să treacă. După cum știți, asta este
validati emailul
. În partea de sus aValidare
clasa, să definim o proprietate publică:public $ email_regex = '/^[\w+-_\.]+@[\w\.]+\.\w+$/';Am pus aceasta într-o proprietate publică, astfel încât, dacă utilizatorul dorește să o înlocuiască cu propriul lor regex, ar putea. Folosesc această versiune simplă a unui regex de e-mail, dar o poți înlocui cu regexul tău favorit dacă vrei.
Apoi, există metoda:
funcția publică validate_email ($ address) returnează preg_match ($ this-> email_regex, $ address) == 1Acum, rulați din nou testele și:
Scrierea mai multor teste
Timp pentru mai multe teste:
Nume de utilizatori
Să facem câteva teste pentru numele de utilizator acum. Cerințele noastre sunt pur și simplu că trebuie să fie un șir de caractere de la 4 până la 20 de caractere constând doar din caractere sau perioade de cuvinte. Asa de:
funcția publică validates_a_good_username () $ result = $ this-> val-> validate_username ("some_user_name.12"); \ Consolidarea \ Assert :: isTrue ($ rezultat);Acum, ce zici de câteva nume de utilizator care nu ar trebui să valideze:
funcția publică rejects_bad_usernames () $ val_username = $ this-> get_scenario ('validate_username'); $ usernames = array ("nume cu spațiu", "nu! exclamation! mark", "ts", "thisUsernameIsTooLongItShouldBeBetweenFourAndTwentyCharacters"); foreach ($ usernames ca $ nume) $ val_username-> cu ($ name) -> așteptați (false); $ val_username-> verifyExpectations ();Acest lucru este foarte asemănător cu al nostru
reject_bad_email_addresses
funcţie. Observați, totuși, că sunăm acest lucruget_scenario
metoda: de unde vin? Abstracționez funcționalitatea creării scenariului în metoda privată, în partea de jos a clasei noastre:funcția privată get_scenario (metoda $) $ val_wrapper = \ Enhance \ Core :: getCodeCoverageWrapper ("Validare"); returnați \ Enhance \ Core :: getScenario ($ val_wrapper, metoda $);Putem folosi acest lucru în nostru
reject_bad_usernames
și înlocuiți crearea scenariului înreject_bad_email_addresses
de asemenea. Deoarece aceasta este o metodă privată, EnhancePHP nu va încerca să o execute ca un test normal, așa cum se va face cu metodele publice.Vom face aceste teste să treacă similar cu modul în care am făcut prima trecere setată:
# În partea de sus ... public $ username_regex = '/^[\w\.]4,20$/'; # și metoda ... funcția publică validate_username ($ username) returnează preg_match ($ this-> username_regex, $ username) == 1;Acest lucru este destul de fundamental, desigur, dar asta e tot ce este necesar pentru a ne atinge scopul. Dacă vrem să ne întoarcem o explicație în caz de eșec, ați putea face ceva de genul:
funcția publică validate_username ($ username) $ len = strlen ($ username); dacă ($ len < 4 || $len > 20) return "Numele de utilizator trebuie să aibă între 4 și 20 de caractere"; elseif (preg_match ($ this-> nume_regex, $ username) == 1) return true; altceva return "Numele de utilizator trebuie să includă numai litere, numere, subliniere sau perioade.";Desigur, s-ar putea să doriți să verificați dacă numele de utilizator există deja.
Acum, executați testele și ar trebui să le vedeți pe toți trecând.
Numere de telefon
Cred că aveți de-a face cu acest lucru până acum, așa că haideți să terminăm exemplul de validare, verificând numerele de telefon:
funcția publică validates_good_phonenumbers () $ val_phonenumber = $ this-> get_scenario ("validate_phonenumber"); $ numere = array ("1234567890", "(890) 123-4567", "123-456-7890", "1234567890", "(123) 4567890"); foreach ($ numere ca $ num) $ val_phonenumber-> cu ($ num) -> așteptați (adevărat); $ val_phonenumber-> verifyExpectations (); funcția publică rejects_bad_phonenumnbers () $ result = $ this-> val-> validate_phonenumber ("123456789012"); \ Consolidarea \ Assert :: isFalse ($ rezultat);Probabil vă dați seama
Validare
metodă:public $ phonenumber_regex = '/ ^ \ d 10 $ | ^ (\ (? \ d 3 \)? funcția publică validate_phonenumber ($ number) returnează preg_match ($ this-> phonenumber_regex, $ number) == 1;Acum, putem face toate testele împreună. Iată cum arată acest lucru din linia de comandă (mediul de testare preferat):
Alte funcționalități ale testului
Desigur, EnhancePHP poate face mult mai mult decât ceea ce ne-am uitat în acest mic exemplu. Să ne uităm la ceva acum.
Am întâlnit foarte scurt
\ Consolidarea \ Assert
clasă în primul nostru test. Nu am folosit-o altfel, pentru că nu este util atunci când folosim scenarii. Totuși, acolo sunt toate metodele de afirmație. Frumusețea lor este că numele lor fac ca funcționalitatea lor să fie incredibil de evidentă. Următoarele exemple de testare ar trece:
\ Enhance \ Assert :: suntIdentice ("Nettuts +", "Nettuts +")
\ Enhance \ Assert :: areNotIdentical ("Nettuts +", "Psdtuts +")
\ Consolidarea \ Assert :: isTrue (true)
\ Consolidarea \ Assert :: isFalse (false)
\ Enhance \ Assert :: conține ("Net", "Nettuts +")
\ Consolidarea \ Assert :: isnull (null)
\ Consolidarea \ Assert :: isNotNull ( 'Nettust +')
\ Enhance \ Assert :: isInstanceOfType ("Excepție", Excepție nouă (""))
\ Enhance \ Assert :: isNotInstanceOfType ("String", o nouă excepție (""))
Există și alte câteva metode de afirmare; puteți verifica docs pentru o listă completă și exemple.
ÎmbunătățireaPHP poate face, de asemenea, mocks și stubs. N-ai auzit de batjocuri și bătăi de cap? Ei bine, nu sunt prea complicați. O machetă este o împachetare pentru obiect, care poate urmări ce metode sunt numite, cu ce proprietăți sunt numite și ce valori sunt returnate. Un băiat va avea un test pentru a verifica, așa cum vom vedea.
Iată un mic exemplu de machetă. Să începem cu o clasă foarte simplă care contează:
num = $ acest-> num + $ num; returnați $ this-> num;
Avem o funcție: creştere
, care acceptă un parametru (dar este implicit la 1) și mărește valoarea $ num
proprietate cu acest număr.
Am putea folosi această clasă dacă construim un tablou de bord:
clasa tabloului de bord public $ home = 0; public $ away = 0; funcția publică __construct ($ home, $ away) $ this-> home_counter = $ home; $ this-> away_counter = $ away; funcția publică score_home () $ this-> home = $ this-> home_counter-> increment (); returnați $ this-> home; funcția publică score_away () $ this-> away = $ this-> away_counter-> increment (); returnați $ this-> home;
Acum, vrem să încercăm să ne asigurăm că Tejghea
exemplu creştere
funcționează corect atunci când Tabloul de bord
metodele instanței o numesc. Așadar, am creat acest test:
clasa ScoreboardTest extinde \ Enhance \ TestFixture funcția publică score_home_calls_increment () $ home_counter_mock = \ Enhance \ MockFactory :: createMock ("Counter"); $ away_counter = nou Counter (); $ home_counter_mock-> addExpectation (\ Enhance \ Expect :: metoda ('increment')); $ scoreboard = tabloul de bord nou ($ home_counter_mock, $ away_counter); $ Scoreboard-> score_home (); $ Home_counter_mock-> verifyExpectations (); \ Îmbunătățiți \ Core :: runTests ();
Observați că începem prin a crea $ home_counter_mock
: folosim fabrica de machete EnhancePHP, dându-i numele clasei pe care ne batem. Aceasta returnează o instanță "înfășurată" din Tejghea
. Apoi, adăugăm o așteptare, cu această linie
$ home_counter_mock-> addExpectation (\ Enhance \ Expect :: metoda ('increment'));
Așteptările noastre spun doar că ne așteptăm creştere
metoda care trebuie apelată.
După aceea, continuăm să creăm Tabloul de bord
instanță și apel score_home
. Atunci noi verifyExpectations
. Dacă executați acest lucru, veți vedea că testul nostru trece.
De asemenea, am putea să afirmăm ce parametri vrem o metodă pe obiectul mock pentru a fi apelat, ce valoare este returnată sau de câte ori ar trebui să fie apelată metoda, cu ceva de genul:
$ home_counter_mock-> addExpectation (\ Enhance \ Expect :: metoda ('increment') -> cu (10)); $ home_counter_mock-> addExpectation (\ Enhance \ Expect :: metoda ('increment') -> ori (2)); $ home_counter_mock-> addExpectation (\ Enhance \ Expect :: metoda ('increment') -> returnează (1)); $ home_counter_mock-> addExpectation (\ Enhance \ Expect :: metoda ('increment') -> cu (3) -> ori (1)); $ home_counter_mock-> addExpectation (\ Enhance \ Expect :: metoda ('increment') -> cu (2) -> întoarce (2));
Ar trebui să menționez asta, în timp ce cu
și ori
va afișa teste eșuate dacă așteptările nu sunt menite, se intoarce
nu. Va trebui să păstrați valoarea returnată și să folosiți o afirmație foarte importantă. Nu sunt sigur de ce este cazul, dar fiecare bibliotecă are quirks :). (Puteți vedea un exemplu în exemplele de bibliotecă din Github.)
Apoi, există niște cioturi. Un stub se umple pentru un obiect și o metodă reală, întorcând exact ceea ce îi spui. Deci, să spunem că vrem să ne asigurăm că noi Tabloul de bord
instanța utilizează corect valoarea pe care o primește creştere
, putem stâna a Tejghea
astfel încât să putem controla ce creştere
va reveni:
clasa ScoreboardTest extinde \ Enhance \ TestFixture funcția publică score_home_calls_increment () $ home_counter_stub = \ Enhance \ StubFactory :: createStub ("Counter"); $ away_counter = nou Counter (); $ home_counter_stub-> addExpectation (\ Enhance \ Expect :: metoda ('increment') -> returnează (10)); $ scoreboard = tablou de bord nou ($ home_counter_stub, $ away_counter); $ result = $ tablou de bord-> score_home (); \ Enhance \ Assert :: suntIdentice ($ rezultat, 10); \ Îmbunătățiți \ Core :: runTests ();
Aici, folosim \ Consolidarea \ StubFactory :: createStub
pentru a crea contorul nostru. Apoi, adăugăm o așteptare la această metodă creştere
va reveni 10. Putem vedea că rezultatul este ceea ce ne-am aștepta, dat codul nostru.
Pentru mai multe exemple de mocks și stub cu biblioteca EnhancePHP, verificați Github Repo.
Ei bine, aceasta este o privire la testarea în PHP, folosind framework-ul EnhancePHP. Este un cadru incredibil de simplu, dar oferă tot ce trebuie să faci niște teste de unitate simple pe codul dvs. PHP. Chiar dacă alegeți o altă metodă / cadru pentru testarea PHP-ului dvs. (sau poate vă rotiți propria!), Sper că acest tutorial a stârnit un interes în testarea codului dvs. și cât de simplu poate fi.
Dar probabil că deja vă testați PHP-ul. Să știm cu toții ce folosiți în comentarii; la urma urmei, suntem cu toții aici să învățăm unii de la alții! Vă mulțumesc foarte mult pentru că ați oprit!