Care-i treaba cu TDD-ul

Care-i treaba cu TDD-ul

duminică, 10 octombrie 2010, 10 al lunii a 10-a a anului 2010 :)

Ce m-a apucat? Păi hai să vă povestesc pentru ce mi-a trebuit mie sucitura asta cu TDD-ul. O să încep cu partea uşoară, care se vede cu ochiul liber: testarea automatizată.

Testarea automatizată

De obicei, după vreo două săptămîni de lucru la un proiect, se adună destul de multă funcţionalitate şi devine din ce în ce mai greu să verifici tot după fiecare modificare, mai ales dacă se întîmplă schimbări care afectează mai multe părţi ale aplicaţiei. Şi dacă ai destul curaj (?) să nu verifici tot, stă în subconştient un vierme care te roade tot zicînd “oare nu s-o fi stricat ceva pe undeva?”. Şi dacă mai e o aplicaţie care e deja în producţie şi se lucrează la modificări, atunci pericolul, şi repectiv îngrijorarea, e şi mai mare. Şi nu ştiu cum pe voi, dar pe mine tare mă consumă îngrijorarea asta. E o stare de stres, şi stresul nu numai că îţi mănîncă o grămadă de energie, dar te şi împiedică să gîndeşti bine, nemaivorbind creativ.

Şi, dacă mă duce capul cum să pun la punct un mecanism de testare automatizată, cum povesteam în articolul precedent, atunci dorm cu mult mai liniştit: după fiecare modificare oricît de mică sau oricît de mare, pot verifica foarte repede dacă mai merge totul cum mergea.

Evident nu pot scrie din start setul ideal de verificări, dar, e OK, imediat cum detectez o scăpare, scriu repejor un test şi gata! iar sunt la linia de plutire. Adică orice problemă mă deranjează doar o singură dată.

TDD—Programarea ghidată de teste

Deci e foarte clar că testarea automatizată îţi uşurează viaţa de programator. Kent Beck însă a mers mai departe de atît şi a inventat TDD-ul: Test Driven Development. Şi şmecheria consta în aceea că înainte de a scrie orice bucăţică de cod, să scrii un test care verifică că codul respectiv (viitor) face ce trebuie să facă. Şi una dintre esenţele acestei abordări este că îţi clarifici foarte bine şi foarte exact ce vrei să faci. Mie de exemplu mi s-a întîmplat de foarte multe ori să încep să programez ceva fără să am o idee clară despre ce anume şi cum urmează să funcţioneze mecanismul respectiv. Desigur şi asta e normal la stadiul de explorare, partea în care doar pipăi şi încerci să-ţi faci o impresie despre ce şi cum se poate face, de exemplu cînd încerci o tehnologie nouă, dar, cînd scrii cod care mai tîrziu se duce în producţie aatunci e mai bine să ştii ce vrei să faci. :)

O altă chestie bună care rezultă din TDD e că de la bun început te gîndeşti cum să scrii codul ca să-l poţi verifica. Asta e elementar de înţeles şi de închipuit atunci cînd te gîndeşti la funcţii şi la clase. Un exemplu banal: vrei o funcţie care să-ţi spună cîte zile în urmă e o anumită dată. Dacă funcţia s-ar numi daysSince testul scris cu QUnit ar arăta cam aşa:

test('Funcţia care arată cîte zile în urmă e o dată', 1, function() { var yesterday = Date('2010-10-09'); equals(daysSince(yesterday), 1, 'A trecut o zi de ieri'); }

După definirea testului, un pas important în TDD e să-l rulăm să vedem că nu trece şi să ne asigurăm că funcţia nu este deja implementată de altcineva. Deci executăm testele: roşu. Bun!

Acum ştim că funcţia ar trebui să ne întoarcă 1 dacă i-am da ca argument data de ieri. În cazuri de complexitate mai mare, nenea Kent ar merge pe calea “fake it till you make it” implementînd funcţia daysSince cam aşa:

function daysSince(date) { return 1; }

Pare anectodic, dar asta respectă principiul “the simplest thing that could possibly work”, şi dacă precăutăm cazul concret pe care-l avem în test, atunci, funcţia face suficient cît să-l treacă. Sună a braşoavă, dar, aveţi răbdare, toate se leagă mai tîrziu foarte bine. ;) Pe deasupra, dacă avem vreo problemă cu implementarea asta, avem la dispoziţie următorul pas din ciclul TDD: refactorizarea. Acum cel mai important lucru de pe lume e să avem verde la teste.

Dar cum la noi funcţia e destul de simplă ca algoritm, o să încercăm să scriem o implementarea reală din prima. Cel mai simplu algoritm care-mi vine în minte e să transform data care vine ca argument în timestamp, transform şi data curentă în timestamp, fac scăderea, împart la numărul de milisecunde dintr-o zi, şi rotunjesc aritmetic:

function daysSince(date) { var dateTimestamp = date.getTime(), currentTimestamp = (new Date).getTime(), millisecondsPerDay = 86400000, difference; difference = (currentTimestamp - dateTimestamp) / millisecondsPerDay; return Math.round(difference); }

Gata. Dacă astea dunt cerinţele, asta e implementarea. Cînd apar alte cerinţe, scriu alte teste şi iar sunt asigurat că funcţia face ce trebuie să facă, iar dacă nu face, o să aflu imediat.

Da, dar, ce pot să fac eu cu TDD-ul în lucrul de zi cu zi?!

Într-adevăr, e una să testezi o funcţie, şi alta e să încerci să testezi codul JavaScript care există de obicei în aplicaţiile web, sau cum să-l scrii testabil. Hai să vedem. Vreau să verific că atunci cînd dau click pe un buton mi se afişeză nişte informaţie suplimentară despre ceva.

test('Verificăm butonul', 2, function() { ok($('div.moreDetails').is(':not(:visible)'), 'Detaliile nu sunt vizibile înainte de a da click pe buton'); $('button.showMoreDetails').click(); ok($('div.moreDetails').is(':visible'), 'Detaliile sunt vizibile după ce se dă click pe buton'); });

Nu e complicat deloc cînd merge vorba de chestii sincrone ca aceasta. Treaba e un pic mai delicată cînd avem chestii asincrone de genul la AJAX, de exemplu dacă detaliile care trebuei afişate, trebuie să le încarc de pe server şi această abordare nu mai merge pentru că după ce simulez un click pe buton, datele încă nu sunt încărcate. Pentru a testa chestii asincrone QUnit are teste asincrone.

test('Verificăm butonul asincron', 2, function() { stop(1000); ok($('div.moreDetails').is(':not(:visible)'), 'Detaliile nu sunt vizibile înainte de a da click pe buton'); $('div.moreDetails').bind('loaded', function() { ok($('div.moreDetails').is(':visible'), 'Detaliile sunt vizibile după ce se dă click pe buton'); start(); }); $('button.showMoreDetails').click(); });

Aici se zice aşa: cînd div.moreDetails o să-mi zică că s-au încărcat datele, atunci o să verific dacă s-au afişat. Pentru ca asta să funcţioneze, în aplicaţie trebuie să declanşăm evenimentul loaded pe div-ul respectiv, şi asta se face elementar cu jQuery:

$('button.showMoreDetails').bind('click', function() { var details = $('div.moreDetails'); details.load('details.html', function() { details.trigger('loaded'); }); });

În aceeaşi manieră se pot testa mecanisme asincrone de orice complexitate.

Pe scurt (mi se termină timpul ;)) TDD-ul ne ghidează spre a scrie cod testabil, ceea ce duce la o mult mai uşoară automatizare a testelor.


Sursa
2010-10-10 17:47:00



Comenteaza





Ultimele 25 posturi adăugate

15:02:24 —» Путепроводные Заметки
08:53:18 —» Путепроводные Заметки
20:09:05 —» Путепроводные Заметки
14:12:49 —» Путепроводные Заметки
07:12:00RĂUL SE FACE, BINELE SE CREEAZĂ —» Leo Butnaru
17:02:12 —» Путепроводные Заметки
16:51:37Balul Mascat de la „Casa Sorelui” – un adevărat spectacol de bucurie! —» Asociaţia Obştească "Demos"
16:25:42Finalizarea procesului de selecție a furnizorilor pentru programul de susținere a tinerilor antreprenori —» Asociaţia Obştească "Demos"
09:25:00I Wish… —» Александр Ищенко - Размышления
06:33:00ÎN GRĂDINA DOMNULUI —» Leo Butnaru
22:55:45 —» Путепроводные Заметки
15:47:15 —» Путепроводные Заметки
14:20:36 —» Путепроводные Заметки
06:59:00STRICTUL NECESAR —» Leo Butnaru
04:40:37 —» Путепроводные Заметки
22:40:20 —» Путепроводные Заметки
17:37:45 —» Путепроводные Заметки
14:57:07 —» Путепроводные Заметки
07:04:00DIN STRICTUL NECESAR —» Leo Butnaru
19:58:25Sfinții Vechiului Testament – Vasile Filat —» Moldova Creștină
18:57:55Folclorul ce se duce —» Biblioteca de Arte 'Tudor Arghezi'
13:43:18BANZAI —» Andrei LANGA. Blogul personal
13:32:00Top 10 fotbaliști moldoveni în 2024 —» Sandu GRECU
08:27:05Unde dispar prietenii noștri!? —» Sergiu Mocanu
08:21:00DIN LEOLOGISME —» Leo Butnaru