Dacă sperați să aflați de ce testele sunt benefice, acesta nu este articolul pentru dvs. De-a lungul acestui tutorial, voi presupune că deja înțelegeți avantajele și speră să aflați cum să vă scrieți și să organizați cele mai bune teste în Laravel 4.
Versiunea 4 a Laravel oferă îmbunătățiri serioase în ceea ce privește testarea, comparativ cu lansarea anterioară. Acesta este primul articol dintr-o serie care va acoperi modul de scriere a testelor pentru aplicațiile Laravel 4. Vom începe seria, discutând testul modelului.
Cu excepția cazului în care executați interogări brute în baza de date, Laravel permite aplicației dvs. să rămână baza de date agnostică. Cu o simplă modificare a driverului, aplicația dvs. poate lucra acum cu alte DBMS-uri (MySQL, PostgreSQL, SQLite etc.). Printre opțiunile implicite, SQLite oferă o caracteristică ciudată, dar foarte utilă: baze de date în memorie.
Cu Sqlite, putem seta conexiunea bazei de date la :memorie:
, ceea ce va accelera drastic testele noastre, datorită bazei de date care nu există pe hard disk. Mai mult decât atât, baza de date de producție / dezvoltare nu va fi populată niciodată cu date de test stânga, deoarece conexiunea, :memorie:
, începe întotdeauna cu o bază de date goală.
Pe scurt: o bază de date în memorie permite teste rapide și curate.
În cadrul app / config / testare
director, creați un nou fișier numit database.php
, și completați-l cu următorul conținut:
// app / config / test / database.php 'sqlite', 'conexiuni' => array ('sqlite' => array ('driver' => 'sqlite', 'database' => ': memory';
Faptul că database.php
este plasat în configurație testarea
înseamnă că aceste setări vor fi utilizate numai atunci când se află într-un mediu de testare (pe care Laravel îl stabilește automat). Ca atare, când aplicația dvs. este accesată în mod normal, baza de date în memorie nu va fi utilizată.
Deoarece baza de date în memorie este întotdeauna goală atunci când se face o conexiune, este important să migra baza de date înainte de fiecare test. Pentru a face acest lucru, deschideți-vă app / teste / TestCase.php
și adăugați următoarea metodă la sfârșitul clasei:
/ ** * Migrează baza de date și setează mailer-ul pentru a "pretinde". * Acest lucru va face ca testele să ruleze rapid. * * / funcția privată prepareForTests () Artisan :: call ('migrate'); Mail :: prefaci (true);
NOTĂ: The
înființat()
metoda este executată de PHPUnit înainte de fiecare test.
Această metodă va pregăti baza de date și va schimba starea lui Laravel Mailer
clasa la pretinde
. În acest fel, Mailer nu va trimite mesaje e-mail reale când se execută teste. În schimb, acesta va înregistra mesajele "trimise".
A finaliza app / teste / TestCase.php
, apel prepareForTests ()
în cadrul PHPUnit înființat()
care se va executa inainte de fiecare test.
Nu uitați
părinte :: SetUp ()
, pe măsură ce suprascriem metoda din clasa părinte.
/ ** * Pregătirea implicită pentru fiecare test * * / funcția publică setUp () parent :: setUp (); // Nu uita asta! $ This-> prepareForTests ();
In acest punct, app / teste / TestCase.php
ar trebui să arate ca următorul cod. Sa nu uiti asta createApplication
este creată automat de Laravel. Nu trebuie să vă faceți griji.
// app / teste / TestCase.php prepareForTests (); / ** * Creează aplicația. * * @return Symfony \ Component \ HttpKernel \ HttpKernelInterface * / funcția publică createApplication () $ unitTesting = true; $ testEnvironment = 'testarea'; returul necesită __DIR __. "/ ... / ... /start.php"; / ** * Migrează baza de date și setează mailer-ul pentru a "pretinde". * Acest lucru va face ca testele să ruleze rapid. * / funcția privată prepareForTests () Artisan :: apel ('migrate'); Mail :: prefaci (true);
Acum, pentru a scrie testele noastre, pur și simplu extindeți testcase
, iar baza de date va fi inițializată și migrată înainte de fiecare încercare.
Este corect să spun că, în acest articol, nu vom urmări TDD proces. Problema aici este didactică, cu scopul de a demonstra cum pot fi scrise testele. Din acest motiv, am ales să dezvăluie mai întâi modelele în cauză și apoi testele aferente. Cred că aceasta este o modalitate mai bună de a ilustra acest tutorial.
Contextul acestei aplicații demo este un simplu blog / CMS, care conține utilizatori (autentificare), postări și pagini statice (care sunt afișate în meniu).
Rețineți că modelul extinde clasa, Ardent, mai degrabă decât elocvent. Ardent este un pachet care permite validarea ușoară, după salvarea modelului (vezi norme $
proprietate).
Apoi, avem publică statică $ fabrică
array, care utilizează pachetul FactoryMuff, pentru a ajuta la crearea obiectelor atunci când se testează.
Ambii Ardentx și FactoryMuff sunt disponibile prin intermediul ambalatorului și compozitorului.
În a noastră Post
model, avem o relație cu Utilizator
model, prin magie autor
metodă.
În cele din urmă, avem o metodă simplă care returnează data, formatată ca "zi lună an".
// app / models / Post.php 'cerut', // Post tittle 'slug' => 'obligatoriu' alpha_dash ', // Post url' content '=>' obligatoriu ', // Post content (' Markdown ')' author_id '=> // ID autor); / ** * Array utilizat de FactoryMuff pentru a crea obiecte de testare * / public static $ factory = array ('title' => 'string', 'slug' => 'string', 'content' => 'text' '=>' fabrică 'Utilizator', // va fi id-ul unui utilizator existent); / ** * aparține utilizatorului * / autorului funcției publice () return $ this-> belongsTo ('Utilizator', 'author_id'); / ** * Obțineți data postare formatată * * @return string * / funcția publică postedAt () $ date_obj = $ this-> created_at; dacă (is_string ($ this-> created_at)) $ date_obj = DateTime :: createFromFormat ('Y-m-d H: i: s', $ date_obj); returnați $ date_obj-> format ('d / m / Y');
Pentru a păstra lucrurile organizate, am plasat clasa cu Post
testele de model în app / teste / modele / PostTest.php
. Vom face toate testele, o secțiune la un moment dat.
// app / teste / modele / PostTest.phpExtinde
testcase
clasa, care este o cerință pentru testarea PHPUnit în Laravel. De asemenea, nu uitați de noiprepareTests
care se va desfășura înainte de fiecare încercare.funcția publică test_relation_with_author () // Instanțiate, umpleți cu valori, salvați și returnați $ post = FactoryMuff :: create ('Post'); // Datorită lui FactoryMuff, această postare $ are un autor $ this-> assertEquals ($ post-> author_id, $ post-> author-> id);Acest test este unul "opțional". Testează faptul că relația "
Post
aparține luiUtilizator
"Scopul aici este de a demonstra în mare măsură funcționalitatea FactoryMuff.Odata ce
Post
clasa aufabrica $
array static conținând'author_id' => 'fabrică' Utilizator '
(notați codul sursă al modelului, prezentat mai sus), FactoryMuff instantează un nouUtilizator
umple atributele sale, salvează în baza de date și în cele din urmă returnează ID-ul laauthor_id
atribut înPost
.Pentru ca acest lucru să fie posibil,
Utilizator
modelul trebuie să aibă afabrica $
array care descrie și câmpurile sale.Observați cum puteți accesa
Utilizator
relație prin$ Post-> autor
. Ca exemplu, putem accesa$ Post-> author-> nume de utilizator
, sau orice alt atribut utilizator existent.Pachetul FactoryMuff permite instanționarea rapidă a obiectelor coerente în scopul testării, respectând și instanțiând orice relație necesară. În acest caz, când creăm a
Post
cuFactoryMuff :: crea ( 'Post')
Utilizator
vor fi, de asemenea, pregătite și puse la dispoziție.funcția publică test_posted_at () // Instanțiate, umpleți cu valori, salvați și returnați $ post = FactoryMuff :: create ('Post'); // Expresie regulată care reprezintă modelul d / m / Y $ expected = '/ \ d 2 \ / \ d 2 \ / \ d 4 /'; // Adevărat dacă preg_match găsește modelul $ matches = (preg_match ($ expected, $ post-> postedAt ()))? adevarat fals; $ this-> assertTrue (meciuri $);Pentru a termina, vom determina dacă șirul returnat de către
postedAt ()
Metoda urmează formatul "zi / lună / an". Pentru o astfel de verificare, o expresie regulată este utilizată pentru a testa dacă modelul\ D 2 \ / \ d 2 \ / \ d 4
("2 numere" + "bar" + "2 numere" + "bar" + "4 numere") e gasit.Alternativ, am putea folosi matricea assertRegExp a PHPUnit.
În acest moment,
app / teste / modele / PostTest.php
fișierul este după cum urmează:// app / teste / modele / PostTest.php assertEquals ($ post-> autor_id, $ post-> autor-> id); funcția publică test_posted_at () // Instanțiate, umpleți cu valori, salvați și returnați $ post = FactoryMuff :: create ('Post'); // Expresie regulată care reprezintă modelul d / m / Y $ expected = '/ \ d 2 \ / \ d 2 \ / \ d 4 /'; // Adevărat dacă preg_match găsește modelul $ matches = (preg_match ($ expected, $ post-> postedAt ()))? adevarat fals; $ this-> assertTrue (meciuri $);PS: Am ales să nu scriu numele testelor în CamelCase pentru scopuri de citire. PSR-1 mă ierți, dar
testRelationWithAuthor
nu este la fel de ușor de citit cum aș prefera personal. Sunteți liber să folosiți stilul pe care îl preferați cel mai bine, desigur.Model de pagină
CMS are nevoie de un model pentru a reprezenta pagini statice. Acest model este implementat după cum urmează:
'obligatoriu', // Titlul paginii 'slug' => 'obligatoriu' alpha_dash ', // Continutul (markdown)' author_id '=> , // ID autor); / ** * Array folosit de FactoryMuff * / public static $ factory = array ('title' => 'string', 'slug' => fabrică | Utilizator ', // va fi id-ul unui utilizator existent.); / ** * aparține utilizatorului * / autorului funcției publice () return $ this-> belongsTo ('Utilizator', 'author_id'); / ** * Redă meniul folosind cache * * @return Html șir pentru link-uri de pagină. * / funcția statică publică renderMenu () $ pages = Cache :: rememberForever ('pages_for_menu', funcția () return Page :: select (array ('title', 'slug' ();); $ result = "; foreach (pagini $ ca pagina $) $ result. = HTML :: action ('PagesController @ show', $ page ['title'], ['slug' => $ page ['slug'] ]). " ($ succes) / ** * uitați cache atunci când ați salvat * / funcția publică dupăSave ($ success) if ($ success) Cache :: forget ('pages_for_menu'); eliminat * / funcția publică șterge () parent delete (); Cache :: forget ('pages_for_menu');Putem observa că metoda statică,
renderMenu ()
, redă un număr de linkuri pentru toate paginile existente. Această valoare este salvată în cheia cache,'Pages_for_menu'
. În acest fel, în viitoarele apeluri cătrerenderMenu ()
, nu va fi nevoie să atingeți baza de date reală. Acest lucru poate oferi îmbunătățiri semnificative ale performanței aplicației noastre.Cu toate acestea, dacă a
Pagină
este salvat sau șters (afterSave ()
șișterge()
metode), valoarea cache-ului va fi eliminată, cauzândrenderMenu ()
pentru a reflecta noua stare de date. Deci, dacă numele unei pagini este modificat sau dacă este șters,cheie 'pages_for_menu'
este șters din memoria cache. (Cache :: uita ( 'pages_for_menu');
)NOTĂ: Metoda,
afterSave ()
, este disponibil prin pachetul Ardent. În caz contrar, ar fi necesar să se pună în aplicareSalvați()
metoda de curățare a cache-ului și a apeluluipărinte :: save ()
;Teste de pagină
În:
app / teste / modele / PageTest.php
, vom scrie urmatoarele teste:assertEquals ($ pagină-> autor_id, $ pagină-> autor-> id);Încă o dată, avem un test "opțional" pentru a confirma relația. Întrucât relațiile sunt responsabilitatea
Illuminate \ Baza de date \ Elocvent
, care este deja acoperit de testele lui Laravel, nu este nevoie să scriem un alt test pentru a confirma că acest cod funcționează așa cum era de așteptat.funcția publică test_render_menu () $ pages = array (); pentru ($ i = 0; $ i < 4; $i++) $pages[] = FactoryMuff::create('Page'); $result = Page::renderMenu(); foreach ($pages as $page) // Check if each page slug(url) is present in the menu rendered. $this->assertGreaterThan (0, strpos ($ rezultat, $ pagină-> slug)); // Verificați dacă cache-ul a fost scris $ this-> assertNotNull (Cache :: get ('pages_for_menu'));Acesta este unul dintre cele mai importante teste pentru
Pagină
model. Mai întâi, patru pagini sunt create înpentru
buclă. Ca urmare, rezultatulrenderMenu ()
apelul este stocat în$ rezultat
variabil. Această variabilă ar trebui să conțină un șir HTML care conține legături către paginile existente.
pentru fiecare
buclă verifică dacă este prezentă urla (url) a fiecărei pagini$ rezultat
. Acest lucru este suficient, deoarece formatul exact al codului HTML nu este relevant pentru nevoile noastre.În cele din urmă, vom determina dacă cheia cache-ului,
pages_for_menu
, are ceva stocat. Cu alte cuvinte, a făcut-orenderMenu ()
apelul a salvat o anumită valoare în memoria cache?funcția publică test_clear_cache_after_save () // O valoare de test este salvată în memoria cache Cache :: put ('pages_for_menu', 'avalue', 5); // Aceasta ar trebui să curețe valoarea în cache $ page = FactoryMuff :: create ('Page'); $ This-> assertNull (Cache :: get ( 'pages_for_menu'));Acest test are scopul de a verifica dacă, atunci când salvați un nou
Pagină
, cheia cache-ului'Pages_for_menu'
este golit.FactoryMuff :: crea ( 'pagină');
în cele din urmă declanșeazăSalvați()
astfel încât să fie suficientă pentru cheia,'Pages_for_menu'
, pentru a fi eliminate.funcția publică test_clear_cache_after_delete () $ page = FactoryMuff :: create ('Page'); // Se salvează o valoare de test în memoria cache Cache :: put ('pages_for_menu', 'value', 5); // Aceasta ar trebui să curețe valoarea în cache $ page-> delete (); $ This-> assertNull (Cache :: get ( 'pages_for_menu'));Similar testului anterior, acesta determină dacă cheia
'Pages_for_menu'
este golit în mod corespunzător după ce a fost ștersPagină
.Ta
PageTest.php
ar trebui să arate așa:assertEquals ($ pagină-> autor_id, $ pagină-> autor-> id); funcția publică test_render_menu () $ pages = array (); pentru ($ i = 0; $ i < 4; $i++) $pages[] = FactoryMuff::create('Page'); $result = Page::renderMenu(); foreach ($pages as $page) // Check if each page slug(url) is present in the menu rendered. $this->assertGreaterThan (0, strpos ($ rezultat, $ pagină-> slug)); // Verificați dacă cache-ul a fost scris $ this-> assertNotNull (Cache :: get ('pages_for_menu')); funcția publică test_clear_cache_after_save () // O valoare de test este salvată în memoria cache Cache :: put ('pages_for_menu', 'avalue', 5); // Aceasta ar trebui să curețe valoarea în cache $ page = FactoryMuff :: create ('Page'); $ This-> assertNull (Cache :: get ( 'pages_for_menu')); funcția publică test_clear_cache_after_delete () $ page = FactoryMuff :: create ('Page'); // Se salvează o valoare de test în memoria cache Cache :: put ('pages_for_menu', 'value', 5); // Aceasta ar trebui să curețe valoarea în cache $ page-> delete (); $ This-> assertNull (Cache :: get ( 'pages_for_menu'));Modelul utilizatorului
Legat de modelele prezentate anterior, avem acum
Utilizator
. Iată codul pentru modelul respectiv:'șir', 'email' => 'email', 'parola' => '123123', 'password_confirmation' => '123123',); / ** * Are multe pagini * / pagini de funcții publice () return $ this-> hasMany ('Page', 'author_id'); / ** * Are numeroase postări * / posturi de funcții publice () return $ this-> hasMany ('Post', 'author_id');Acest model lipsește testelor.
Putem observa că, cu excepția relațiilor (care pot fi utile pentru testare), nu există nici o implementare a metodei aici. Cum rămâne cu autentificarea? Utilizarea pachetului Confide oferă deja implementarea și testele pentru acest lucru.
Testele pentru
Zizaco \ Încredeți \ ConfideUser
sunt situate în ConfideUserTest.php.Este important să determinați responsabilitățile de clasă înainte de a vă scrie testele. Testarea opțiunii pentru "resetați parola" a
Utilizator
ar fi redundant. Acest lucru se datorează faptului că responsabilitatea corespunzătoare pentru acest test este înăuntruZizaco \ Încredeți \ ConfideUser
; nu înUtilizator
.Același lucru este valabil și pentru testele de validare a datelor. Deoarece pachetul, Ardent, se ocupă de această responsabilitate, nu ar avea sens să testeze din nou funcționalitatea.
Pe scurt: testați-vă curat și organizat. Determinați responsabilitatea corespunzătoare a fiecărei clase și testați numai ceea ce este strict responsabilitatea acesteia.
Concluzie
Utilizarea unei baze de date în memorie este o bună practică pentru a executa rapid testele împotriva unei baze de date. Datorită ajutorului oferit de unele pachete, cum ar fi Ardent, FactoryMuff și Confide, puteți minimiza cantitatea de cod din modelele dvs., păstrând în același timp testele curate și obiective.
În continuarea acestui articol, vom examina Controlor testare. Rămâneți aproape!
Încă începeți cu Laravel 4, să vă învățăm lucrurile esențiale!