Chestia este ca TDD-ul nu e un one size fits all. Depinde de faza in care e proiectul deobicei si de cat de sigur vreti sa fiti cand mergeti inainte. Disclaimer: consider ca dev fara teste nu exista si ca si arhitect/senior dev trebuie sa faci testarea sa fie un proces cat mai smooth si seamless pentru ceilalti developeri. Asta inseamna ca testele trebuie sa fie cat de cat rapide, usor de rulat, conclusive si, mai ales, consistente.
Exista tot timpul un trade-off la teste. De exemplu, sa zicem ca ai o ora sa scrii un test.
In ora aia poti sa scrii un test care acopera cum se proceseaza o plata, sau un test care acopera prezenta butonului de plata.
-
Ce e mai valoros pentru business-ul tau?
Sa nu gaseasca clientul butonul de plata e destul de nasol, nu? Asa n-o sa fii niciodata platit si daca nu ai QA sa-ti zica ca iti lipseste butonul, o sa te miri de ce nu-ti intra banii.
Destul de nasol. Dar, mai nasol este, clientul sa plateasca si sa vada ca i s-au tras din cont 1k USD in loc de 1 USD, sau mai rau clientul sa vada ca a platit dar tie sa nu-ti intre banii.
Un buton poti sa-l pui usor cand observi lipsa lui, dar un sistem de plata e mai greu de rezolvat.
-
Ce e mai valoros pentru stabilitatea sistemului tau? Aici ma refer si la long-term, care dintre teste o sa te ajute mai mult cand tu schimbi ca nebunul codul si ai nevoie de siguranta ca nu strici nimic din ce merge si e important?
Well, un buton lipsa sau in plus, nu deranjeaza decat clientul (joke) . Imagineaza-ti cat de deranjant e pentru tine cand, inainte de un deploy sau merge, o sa rogi pe toata lumea sa mai testeze odata ca ti-e frica ca poate ai schimbat ceva critic. Timpul si linistea ta costa. Asta e un semn ca la un moment dat ai ales sa scrii testul gresit sau ca nu l-ai scris deloc
Un exemplu poate stupid, dar o ora pe testul gresit cand nu ai mult timp de bagat in teste poate sa te impacteze foarte mult. Ah si, desigur, niciodata nu scrii testele si uiti de ele.
Testele sunt cod. Codul trebuie intretinut. Asta inseamna ca ambele exemple de mai sus trebuie intretinute. Poate la un moment dat o sa-ti pice random testul, o sa trebuiasca sa-l rezolvi ca sa te asiguri ca problema nu e de la codul din spate.
Deci toate testele sunt un trade-off intre timpul devului (care e cel mai valoros dpdv banesc intr-un proiect), si linistea sufleteasca pe care o sa o ai pentru business-ul tau si sistemul tau.
Code-coverage e o minciuna. Lumea reala e complicata si oamenii o sa-ti foloseasca softul intr-un fel in care nu te astepti. Poti sa ai code-coverage 100% si totusi sa ai flow-uri pe care nu le acoperi. Ba chiar, in cursa pentru code-coverage cat mai mare, iti inchizi sistemul la modificari. Ca sa-ti faci codul unit-testable, trebuie sa scrii intr-un anumit fel, sa iti faci totul mockuibil, samd. Toate lucrurile acestea fac dificila orice modificare. Si frustranta in acelasi timp, ca stii ca atunci cand faci o modificare o sa-ti pice mii de teste care defapt nu-s importante.
Exemplu de code-coverage 100% care e gresit in lumea reala. Sa zicem ca faci un unit-test unde testezi cum intra o comanda intr-un ecommerce si iti rezerva produsele. Faci totul testabil, mockuiesti, scrii testul, totul ok. WRONG. In lumea reala ai o baza de date, ai multiplii clienti, ai lock-uri, samd. Cand iti intra 2 comenzi in acelasi timp pe acelasi produs ce se intampla?
Merita sa faci totul testabil unitar? Dupa ce te lovesti de un caz de genul, incepi sa te gandesti la trade-off-ul de timp: faci totul unit-testable (care ia considerabil mai mult timp) vs testezi sistemul asa cum e si castigi niste timp, chiar daca nu castigi siguranta 100% peste tot, dar esti sigur ca ai un caz critic care nu va crapa.
Testele unitare sunt abstractizate de lumea reala, ele nu reprezinta neaparat un adevar ci o extra siguranta pentru tine. De-aia faci mock-uri, samd. Exemple pentru teste unitare bune: calculul totalului unei comenzi, calculul unui discount din total, daca un dto face o mapare buna, daca o clasa se poate instantia, daca se copiaza bine in memorie, etc. In principiu sunt bune ca sunt rapide is testeaza codul in sine( in nici un caz sistemul real ). Pentru ca testeaza codul in sine, ele sunt reliable ca singura variabila programatorul si modificarile aduse de el.
Testele de integrare sunt lente. Sau asa ar spune legenda. Traim intr-o lume in care iti poti rula majoritatea stack-ului local gratie Docker & friends, iar daca iti ruleaza local de ce ar fi foarte incet un test de integrare? Exista o penalizare de timp gratie lumii reale, dar macar asa esti sigur ca timpul ala in care faci o comanda e exact cam cat asteapta si clientul dupa request. Nu sunt asa de reliable ca si unit testele. Cum ai facuta infrastructura, ceva networking failures pot sa iti dea erori, db-ul poate sa-ti ramana in urma, testele se pot calca pe coada unele pe altele, etc. Aici depinde si de maiestria ta. Aici nu poti sa vorbesti de code-coverage ca iti testezi sistemul ca si un black box, ca si cum l-ar utiliza cineva.
SPAs and testing. Sau cum practic singura ta varianta sunt testele unitare acolo. SPA-urile, care sunt basically JS au nevoie de un browser sa mearga. Daca credeai ca testele de integrare sunt incete, stai sa vezi e2e testingul E slow, e destul de unreliable, browserele faileaza, testele trebuiesc rerulate, memory leakuri, containere care iti crapa random, etc. Practic, aceleasi probleme pe care le ai cu chrome-ul o sa le ai si cu e2e testingul. Deci nu prea poti sa testezi lumea reala, exact ce face un utilizator foarte usor. (P.S. Daca cineva are update-uri pe tema asta, please share, eu nu m-am mai uitat de 1 an pe asta, poate au aparut niste chestii noi care chiar merg in Docker)
Iti inteleg frustrarea, stay strong TDD-ul e misused deobicei ca si plasa de siguranta de catre managerii care nu stiu ce se intampla prin cod. Cumva trebuie sa-i intelegi si pe ei ca mizeria aia de code-coverage e singura statistica legata de cod si implementare care chiar o inteleg mai profund (doar nu te astepti sa se uite chiar atent pe code-review? ) si care chiar le dau siguranta.
TDD-ul folosit bine, trebuie sa permita echipei sa dezvolte intr-un ritm rapid produsul fara frica de a strica ceva pentru a putea face deployuri dese si multe. Deasemenea, cel mai important: nu trebuie sa le intre developerilor in cale. Cred ca aici multi manageri/arhitecti esueaza ca se uita doar la bottom-line si le este frica/nu au incredere in echipa si de asta se bazeaza pe code-coverage ca un fel de salvare miraculoasa de la esec.