Ghidul lui Newbie privind dezvoltarea bazată pe teste

Testarea codului dvs. este enervant, dar impactul de a nu face acest lucru poate fi ordine de mărime mai enervant! În acest articol, vom folosi dezvoltarea bazată pe test, pentru a scrie și testa codul nostru mai eficient.


Ce este dezvoltarea bazată pe teste?

De la începutul erei computerelor, programatorii și bug-urile s-au luptat pentru supremație. Este un eveniment inevitabil. Chiar și cei mai mari programatori cad pradă acestor anomalii. Niciun cod nu este sigur. De aceea facem teste. Programatorii, cel puțin cei sane, își testează codul executându-l pe mașinile de dezvoltare pentru a se asigura că face ceea ce trebuia să facă.


Sane programator care își testează programele.
Imagine de la http://www.youthedesigner.com
Programator nebun care nu-și testează programele.
Imagine de la http://www.internetannoyanceday.com

Dezvoltare bazată pe dezvoltare este o tehnică de programare care vă cere să scrieți simultan codul actual și codul de testare automatizat. Acest lucru vă asigură că vă testați codul și vă permite să vă retestați codul rapid și ușor, deoarece este automatizat.

Cum functioneazã?

Dezvoltarea bazată pe dezvoltare, sau TDD așa cum o vom numi de acum încolo, se învârte în jurul unui ciclu de dezvoltare iterativ scurt, care merge cam așa:

  1. Înainte de a scrie orice cod, mai întâi trebuie să scrieți un test automat pentru codul dvs. În timp ce scrieți testele automate, trebuie să țineți cont de toate intrările, erorile și ieșirile posibile. În acest fel, mintea voastră nu este întunecată de niciun cod care a fost deja scris.
  2. Prima dată când executați testul automat, testul ar trebui să nu funcționeze - indicând faptul că codul nu este încă pregătit.
  3. După aceea, puteți începe programarea. Deoarece există deja un test automat, atâta timp cât codul nu reușește, înseamnă că încă nu este gata. Codul poate fi fixat până când trece toate afirmațiile.
  4. Odată ce codul trece testul, puteți începe să îl curățați prin intermediul refactorizării. Atâta timp cât codul trece în continuare testul, înseamnă că funcționează încă. Nu mai trebuie să vă faceți griji cu privire la modificările care introduc noi erori.
  5. Începeți totul cu o altă metodă sau program.

Ciclul de dezvoltare bazat pe teste
Imagine de la http://en.wikipedia.org/wiki/Test-driven_development

Mare, dar cum este mai bine decât testarea obișnuită?

Ați încercat vreodată să testați un program deoarece:

  • Ai simțit că a fost o pierdere de timp pentru a testa, deoarece a fost doar o schimbare ușoară a codului?
  • Te-ai simțit leneș, încercând din nou totul?
  • Nu ai avut timp suficient pentru a testa, deoarece managerul de proiect a vrut să se mute până la producție ASAP?
  • Ți-ai spus că o vei face "mâine"?
  • A trebuit să alegeți între testarea manuală sau vizionarea celui mai recent episod al emisiunii TV preferate (Big Bang Theory)?

De cele mai multe ori, nu se întâmplă nimic, și vă mutați cu succes codul la producție fără probleme. Dar uneori, după ce te-ai mutat la producție, totul merge prost. Sunteți blocați fixând o sută de găuri într-o navă scufundată, cu mai multe apariții în fiecare minut. Tu faci nu doriți să vă aflați în această situație.


Înșurubați-o, doar mutați-o în producție!
Imagine de la http://phenomenaonbreak.wordpress.com

TDD a fost menită să elimine scuzele noastre. Când un program a fost dezvoltat folosind TDD, ne permite să facem schimbări și să încercăm rapid și eficient. Tot ce trebuie să facem este să executați testele automate și voila! Dacă trece toate testele automate, atunci suntem bine să mergem - dacă nu, atunci înseamnă doar că am rupt ceva cu schimbările. Cunoscând care părți exacte ale testului au eșuat, ne permite, de asemenea, să identificăm cu ușurință ce parte din schimbările s-au rupt, ceea ce face ca fixarea bugurilor să fie mai ușoară.


Sunt vândut. Cum facem acest lucru?

Există o multitudine de cadre de testare automate PHP pe care le putem folosi. Unul dintre cele mai utilizate cadre de testare este PHPUnit.

PHPUnit este un cadru de testare excelent, care poate fi ușor integrat în propriile proiecte sau alte proiecte construite pe baza cadrelor PHP populare.

Cu toate acestea, pentru scopurile noastre, nu vom avea nevoie de multitudinea de funcții oferite de PHPUnit. În schimb, vom opta pentru a crea testele noastre folosind un cadru de testare mult mai ușor, numit SimpleTest.

În pașii următori, să presupunem că dezvoltăm o aplicație de cărți de oaspeți, în care orice utilizator poate adăuga și vedea intrări din cărțile de vizită. Să presupunem că marcarea a fost terminată și că pur și simplu facem o clasă care conține aplicație logică a cărții de oaspeți, unde aplicația se inserează și citește în baza de date. Partea de lectură a acestei clase este ceea ce vom dezvolta și vom testa.


Pasul 1. Configurați SimpleTest

Acesta este, fără îndoială, cel mai ușor pas al tuturor. Chiar și acest tip ar putea face acest lucru:


Pot sa fac asta? Pot să-l folosesc, eu? creier!
Imagine de la http://longstreet.typepad.com/

Descărcați SimpleTest aici și extrageți-l într-un dosar la alegere - de preferință folderul în care vă veți dezvolta codul sau PHP include_path pentru acces ușor.

Pentru acest tutorial, am creat dosarul ca acesta:

Index.php va rula guestbook.php și va invoca metoda de vizualizare și va afișa înregistrările. În interiorul directorului de clase este locul unde vom pune clasa guestbook.php, iar dosarul de testare este locul unde plasăm cea mai simplă bibliotecă.


Pasul 2. Planificați-vă atacul


Imagine de la http://connections.smsd.org/veterans

Al doilea pas, care este de fapt cel mai important, este de a începe să vă creați testele. Pentru aceasta, trebuie să vă planificați și să vă gândiți la ceea ce va face funcția dvs., ce posibile intrări va obține și rezultatele pe care le va trimite. Acest pas seamănă cu jocul de șah - trebuie să știți totul despre adversar (programul), inclusiv toate slăbiciunile sale (erori posibile) și punctele forte (ce se întâmplă dacă se execută cu succes).

Deci, pentru aplicația noastră de cărți de oaspeți, să lăsăm schemele:

Vedere

  • Această funcție nu va conține nici o intrare, deoarece va prelua toate intrările din baza de date și va trimite înapoi datele care urmează a fi tipărite.
  • Acesta va întoarce o serie de înregistrări din cărțile de vizită, afirmând numele posterului și mesajul său. Dacă nu există înregistrări, atunci ar trebui să returneze o matrice goală.
  • Dacă există înregistrări, matricea va avea una sau mai multe valori în ea.
  • În același timp, matricea va avea o structură specifică, cum ar fi:
 Array (['name'] = "Bob" ['message'] = "Bună ziua, sunt Bob" ['message'] = "Bună, sunt Tom"))

Pasul 3. Scrieți un test!


Imagine de la http://cflhomeless.wordpress.com

Acum, putem scrie primul nostru test. Să începem prin crearea unui fișier numit guestbook_test.php în dosarul de testare.

  

Apoi, să convertim ceea ce am stabilit de la pasul doi,.

 adaugă ("Bob", "Bună, sunt Bob"); $ guestbook-> add ("Tom", "Bună, sunt Tom"); $ entries = $ guestbook-> viewAll (); $ count_is_greater_than_zero = (număr ($ intrări)> 0); $ This-> assertTrue ($ count_is_greater_than_zero); $ this-> assertIsA ($ intrări, 'array'); foreach ($ intrări ca intrare $) $ this-> assertIsA ($ entry, 'array'); $ This-> assertTrue (isset ($ intrare [ 'nume'])); $ This-> assertTrue (isset ($ intrare [ 'mesaj']));  function testViewGuestbookWithNoEntries () $ guestbook = carte de oaspeți nouă (); $ Guestbook-> deleteAll (); // Ștergeți mai întâi toate intrările astfel încât să știm că este o tabelă goală $ entries = $ guestbook-> viewAll (); $ this-> assertEqual ($ intrări, array ()); 

Aserțiunile vă asigură că un anumit lucru este ceea ce ar trebui să fie - în principiu, se asigură că ceea ce se întoarce este ceea ce așteptați să revină. De exemplu, dacă o funcție ar trebui să se întoarcă la adevărat dacă are succes, atunci în testul nostru ar trebui afirma că valoarea returnată este egală cu cea reală.

După cum puteți vedea aici, testarea vizualizării cărții de oaspeți cu intrări și fără. Verificăm dacă aceste două scenarii ne transmit criteriile noastre de la pasul doi. Probabil că ați observat că fiecare dintre funcțiile noastre de testare începe cu cuvântul "test". Am făcut acest lucru pentru că, atunci când SimpleTest rulează această clasă, va căuta toate funcțiile care încep cu cuvântul "test" și o execută.

În clasa noastră de test, am folosit și câteva metode de afirmație, cum ar fi assertTrue, assertIsA și assertEquals. Funcția assertTrue verifică dacă o valoare este sau nu adevărată. AssertIsA verifică dacă o variabilă este de un anumit tip sau clasă. În cele din urmă, AssertEquals verifică dacă o variabilă este total egală cu o anumită valoare.

Există alte metode de afirmare oferite de SimpleTest, care sunt:

assertTrue ($ x) Fail dacă $ x este falsă
assertFalse ($ x) Fail if $ x este adevărat
assertNull ($ x) Fail if $ x este setat
assertNotNull ($ x) Fail dacă $ x nu este setat
assertIsA ($ x, $ t) Fail if $ x nu este clasa sau tipul $ t
assertNotA ($ x, $ t) Fail dacă $ x este de clasă sau tip $ t
assertEqual ($ x, $ y) Fail dacă $ x == $ y este falsă
assertNotEqual ($ x, $ y) Fail if $ x == $ y este adevărat
assertWithinMargin ($ x, $ y, $ m) Fail dacă abs ($ x - $ y) < $m is false
assertOutsideMargin ($ x, $ y, $ m) Fail dacă abs ($ x - $ y) < $m is true
AssertIdentical ($ x, $ y) Fail dacă $ x == $ y este falsă sau o nepotrivire de tip
assertNotIdentical ($ x, $ y) Fail dacă $ x == $ y este adevărată și tipurile se potrivesc
assertReference ($ x, $ y) Fail unless $ x și $ y sunt aceeași variabilă
assertClone ($ x, $ y) Fail unless $ x și $ y sunt copii identice
assertPattern ($ p, $ x) Fail unless regex $ p se potrivește cu $ x
assertNoPattern ($ p, $ x) Fail if regex $ p se potrivește cu $ x
expectError ($ x) Înghite orice eroare de potrivire viitoare
afirma ($ e) Respingerea obiectului de așteptare eșuat $ e

Modalitatea de afirmare este curtoazie de http://www.simpletest.org/en/unit_test_documentation.html


Pasul 4. Nu reușesc să câștige


Imagine de la http://verydemotivational.com

După ce ați terminat de scris codul, trebuie să executați testul. Prima dată când executați testul, acesta Ar trebui să nu reușească. Dacă nu, înseamnă că testul dvs. nu testează nimic.

Pentru a rula testul, pur și simplu alerga guestbook_test.php în browserul dvs. Ar trebui să vedeți mai întâi acest lucru:

Acest lucru sa întâmplat deoarece nu am creat încă clasa noastră de cărți de oaspeți. Pentru a face acest lucru, creați guestbook.php în dosarul dvs. de clase. Clasa ar trebui să conțină metodele pe care intenționăm să le folosim, dar nu ar trebui să conțină nimic la început. Țineți minte, mai întâi scriem testele inainte de scriind orice cod.

  

Când executați din nou testul, ar trebui să pară așa:

După cum vedem aici, testul nostru este acum câștigat prin eșec. Aceasta înseamnă că testul nostru este acum pregătit pentru a primi răspuns ".


Imagine de la http://www.gamercastnetwork.com/forums

Pasul 5. Răspundeți-vă testul scriind codul


La un moment dat, cu toții am simțit acest lucru atunci când programăm.
Imagine de la http://fermentation.typepad.com/fermentation

Acum, că avem un test automat de lucru, putem începe să scriem cod. Deschide-ți guestbook.php clasați și începeți să creați răspunsul la testul dvs..

  "Kirk", "mesaj" => "Bună, eu sunt Kirk." ), array ('name' => 'Ted', 'message' => 'Bună ziua, eu sunt Ted')); funcția publică viewAll () // Aici, ar trebui să preluăm toate înregistrările din baza de date. // Aceasta se simulează prin returnarea sumelor $ _entries return return self :: $ _ entries;  funcția publică adăugă ($ name, $ message) // Aici simulează inserarea în baza de date prin adăugarea unei noi înregistrări în $ _entries array // Acesta este modul corect de a face acest lucru: self :: $ _ entries [ = array ('name' => $ nume, 'mesaj' => mesaj $); auto :: $ _ intrări [] = array ('notname' => $ name, 'notmessage' => mesaj $); // oops, există un bug aici undeva return adevărat;  funcția publică deleteAll () // Tocmai am setat matricea $ _entries pentru a simula self :: $ _ entries = array (); return true; 

Această clasă guestbook.php are unele bug-uri în ea în scop, așa că putem vedea cum arată dacă testul nostru eșuează.

Odată ce ne facem testul, ar trebui să vedem ceva de genul:

Ieșirea de testare ne arată în ce test și în care afirmația noastră a eșuat. Din aceasta, putem observa cu ușurință că linia 16 și 17 este afirmația care a aruncat eroarea.

 assertTrue (isset ($ intrare [ 'nume'])); $ This-> assertTrue (isset ($ intrare [ 'mesaj'])) ;? 

Acest lucru ne spune în mod clar că matricea de intrare returnată nu a avut cheia de matrice corectă. Pe baza acestui fapt, vom ști cu ușurință care parte din codul nostru a mers prost.

  $ nume, 'mesaj' => $ mesaj); //fix! return true; ? 

Acum, când conducem din nou testul, ar trebui să ne arate:


Pasul 6. Refactor și perfecționează codul


Imagini sunt multumite de http://www.osborneink.com si http://phuketnews.phuketindex.com

Din moment ce codul pe care îl testăm aici este destul de simplu, testarea noastră și fixarea bug-urilor nu a durat foarte mult. Dar dacă aceasta ar fi o aplicație mai complexă, va trebui să faceți mai multe modificări în codul dvs., să îl faceți mai curat, astfel încât să fie mai ușor de întreținut și multe alte lucruri. Problema cu aceasta, însă, este că schimbarea introduce de obicei bug-uri suplimentare. Aici intră testul nostru automatizat - odată ce facem schimbări, putem face din nou testul. Dacă încă mai trece, înseamnă că nu am nimicit nimic. Dacă eșuează, știm că am făcut o greșeală. De asemenea, ne informează unde este problema și, sperăm, cum o vom putea rezolva.


Pasul 7. Clătiți și repetați


Imagine de la http://www.philstockworld.com

În cele din urmă, atunci când programul dvs. necesită funcționalități noi, va trebui să scrieți noi teste. Asta e ușor! Clătiți și repetați procedurile de la pasul doi (deoarece fișierele dvs. SimpleTest ar trebui deja configurate) și începeți din nou ciclul.


Concluzie

Există mult mai multe articole de dezvoltare în profunzime, bazate pe testare, și chiar mai multă funcționalitate pentru SimpleTest decât ceea ce a fost afișat în acest articol - lucruri precum obiecte machete, stubs, care fac mai ușor crearea testelor. Dacă doriți să citiți mai mult, pagina de dezvoltare bazată pe test Wikipedia vă va stabili pe calea cea bună. Dacă sunteți interesat să utilizați SimpleTest ca cadru de testare, căutați documentația online și asigurați-vă că ați examinat celelalte funcții.

Testarea este o parte integrantă a ciclului de dezvoltare, totuși este prea adesea primul lucru care trebuie tăiat atunci când termenele limită sunt iminente. Sperăm că, după ce ați citit acest articol, veți aprecia cât de util este să investiți în dezvoltarea bazată pe teste.

Care sunt gandurile tale privind dezvoltarea bazata pe teste? Este ceva care vă interesează implementarea sau credeți că este o pierdere de timp? Anunță-mă în comentariile!

Cod