Ce faceți cu unit testele?

In ultimul timp cu react și redux am deaface cu foarte multe fișiere schimbate la orice modificare. Clientul cere coverage de 80% in pipeline, la code review imediat comentează cineva dacă ceva nu e acoperit.

Totuși după ceva timp mi-am dat seama că îmi pierd tot cheful după ce am făcut task-ul și trebuie scrise unit teste la 20 de fișiere. Majoritatea cu reducere la state și funcții asincron în thunks.

Problema e că după ce îmi pierd interesul pierd timpul aiurea cu alte lucruri…

Nici nu se pune problema să scriu unit teste înainte de cod, e mult prea complexa aplicația cu prea multe componente integrate ca să știi unde să scrii logica înainte sa o implementezi.

1 Like

Si mie imi vine greu sa le scriu si trec prin acelasi lucru ca si tine. Am de facut unit teste si de integrare pt un serviciu in aplicatie. Nu folosesc stack-ul tau, ci Java, Spring si Junit. Nu prea am experienta cu partea de testare.

Pana la urma ma impac cu ideea ca trebuie sa le scriu si o iau usor.

1 Like

Da, TDD e cam poveste in multe cazuri…

3 Likes

Eu abia dupa 3 ani, dupa ce am trecut de la un limbaj la altul, am inceput sa fac teste. Asta pentru ca, la noua companie, pentru a pune o aplicatie in productie, e nevoie de un minimum de teste. Mi-am batut capul vreo doua zile pana am prins treaba cu mock-uirea serviciilor. Pentru cineva care e la inceput, e destul de greu sa faci diferenta intre teste corecte si teste inutile (ca unit teste).

Daca ai o aplicatie mare, o sa-ti ia timp si probabil o sa te si plictisesti sa te apuci acum sa faci teste. Daca pornesti de la 0, pare mai simplu ca pentru fiecare serviciu nou implementat, sa-i faci si testele imediat.

Ma uitasem odata la un video al lui Victor Rentea si spunea ca testele se fac dinaintea implementarii. Pe entitati, da, poti sa testezi validarile, actiunile. Par chiar misto, si te ajuta sa descoperi cazuri noi. Dar nu serviciile sau repourile, pentru ca pentru astea ai nevoie de date mockuite. Ori nu imi dau eu seama cum ai putea sa testezi ceva ce nu exista :crazy_face:

1 Like

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.

  1. 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.

  2. 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 :man_shrugging: (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 :laughing:

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 :laughing: 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 :metal: 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? :laughing: ) 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.

10 Likes

M-am ocupat un an bun cu QA, timp in care am dezvoltat propriul framework de testare e2e pentru un proiect foarte dificil de testat, adică nu azi m-am apucat de testare.

Acum tot eu scriu unele teste e2e, dar problema mea e cu unit testele (in special cele care verifica mutațiile in state - e muncă de sisif).

Probabil o strategie ar fi să scriu testele după ce am depășit un anumit număr de fișiere schimbate.
Doar că la o sesiune de peer programming o să depășesc sigur numărul propus.

Eu îmi limitez scopul unit testelor la documentarea codului și la asigurarea că dacă se schimbă/șterge o linie de cod atunci nu afectează altceva. Nu ține neapărat de calitatea produsului final, dar categoric e util la refacturare.

Nu trebuie sa testezi logica, ci input->output

TDD este o poveste frumoasa dupa ce depasesti un anumit nivel de complexitate al aplicatiei…si mai ai si sprinturi de 2 saptamani :smiley:

On topic si strict ce as face eu, as renunta la unit si m-as concentra pe niste teste nitel mai sus in piramida, ceva gen integration.

Si da, code-coverage este o minciuna. Mai ales daca ajungi sa ai un numar ne-negociabil batut in cuie in pipeline. Code coverage este ok daca este vazut ca un reper.

Pe de cealalta parte, daca ajungi sa modifici constant aproximativ 20 de fisiere pentru adaugarea/modificarea unui use-case, poate ar fi cazul sa revizuiti putin arhitectura si sa faceti un refactoring. Cred ca si tu te-ai gandit la asta.

1 Like

E destul de vechi subiectul, chiar foarte vechi, nu mai am acelasi proiect, acum nu mai am aceleasi probleme, acum am altele care se leaga strict de CI/CD, jest si testele care sunt foarte sensibile la resursele disponibile pe server :smiley: Plus monorepo/ distributed task execution / coverage distribuit…
(pe React testele cu react-testing-library si testarea hook-urilor nu sunt unit teste in adevaratul sens, testezi componente izolat, dar randezi virtual componenta cu JSDOM si faci timing la hook-uri, ceea ce e foarte sensibil la resurse si state of the world)

Problema cu modificarea a 20 de fisier era cu testarea cu enzyme si redux peste tot, daca vrei coverage testezi ca fiecare reducer face ce trebuie cu state-ul. Iar la ce feature-uri aveam era mult state de schimbat de la un task la altul.

Practic am rezolvat toate problemele de dinainte, dar acum ma bat cu runnerii de CI/CD fiindca din cauza lor testele cad random si nu se poate dovedi cand un test e flaky sau nu. Din pacate n-am gasit nici o solutie buna de test analytics/lifecycle management, e un posibil proiect de weekend.

Unit Testing-ul il vad din categoria task-urile care trebuiesc facute, nu ca ar fi foarte fun. O vad ca si o migaleala, sau repetitie, de cele mai multe ori, si mai putin un proces de research/creatie, acolo unde imi face cea mai mare placere. Asta, e unul din motivele pentru care am inceput sa indragesc mai mult partea de DevOps decat programming.

1 Like

Sunt bune si unit testele, ca se pot rula usor, dar intr-adevar, mai utile sunt integration tests. Sa va povestesc ultima patzanie: la noi pe plantatie avem un bot care face PRs automat cu update la dependecies. In echipa a aterizat un proiect nou, facut de alta echipa, care se apropia de deadline pentru v1 si unde oamenii dadeau bice pentru a termina ce era de facut pt v1. Zero tests. Vine bot-ul, face PR cu update la libs, imi vine ideea aleatoriu sa aduc branch-ul local si sa pornesc aplicatia (Spring Boot backend API). Aplicatia nu mai porneste, un update a introdus o dependenta ciclica, dar pipeline-ul in Bitbucket era verde, oricine putea face merge pe PR. Nu era treaba mea sa rezolv asta, ba chiar se putea renunta la dependenta aia, m-a interesat mai degraba sa avem un test basic pus la punct. Asa ca am pus pe picioare infrastructura pt integration tests (in Spring Boot e un pic de munca cand folosesti servicii AWS si partea de Security) si am trantit cel mai basic test, ala cu contextLoads(), care pur si simplu verifica daca aplicatia porneste.
Cand ai teste relevante, esti mult mai linistit cand faci un merge sau cand faci deploy. Iar cand ai buguri, poti scrie un test care sa scoata in evidenta bug-ul si apoi sa repari eroarea.

2 Likes

Testele numai in container, plus dagger.io pentru a le rula local (este sdk de nodejs). Sau pentru heavyweights nix ca sa fie garantata reproducibility, si atunci nici nu mai ai nevoie de docker neaparat.

2 Likes

Folositit https://www.testcontainers.org/ ?
La mine pe proiect se foloseste.

1 Like

testele sunt ca banii din marketing-ul, 50% sunt degeaba, dar nu stii care 50%

2 Likes

Da. Foarte usor de pus baza de date, mai usor decat intr-un docker container separat care sa porneasca cand executi integration tests.

Super!
La mine, tinem un Redis.
Ce folosesti pt testare?
Eu am asa

  • Wiremock
  • RestAssured
  • Twitter Finagle - O bibloteca pt call-uri http. De acolo este o clasa Dtab, care are o metoda ce ruteaza call-urile la localhost unde eu definesc un endpoint (Rest Controller clasic de spring)

Pe frontend as zice ca sunt critice. Cand lucrezi cu librarii gen React sau Angular inevitabil o sa faci componente care le refolosesti in nspe locuri. Daca vreodata modifici componenta respectiva nu te apuci sa faci regression manual peste tot unde e folosita.

Fara unit teste nici nu vad cum ai putea sa faci reodata refactoring. Am lucrat pe proiecte fara si era dezastru dupa o anumita complexitate. Se pune si problema ca daca nu ai teste nu ai documentatie pentru toate use caseurile unitatii respective. Daca ai facut o pagina acuma 2 ani si are nspe butoane care in functie de cum sunt apasate fac o gramada de branching paths bafta la modificat sau daugat functionalitate fara teste

3 Likes

In principal Postgres si Mokito si parca RestTemplate sau cum se numea chestia aia din Spring. Sincer au trecut niste luni de cand am lucrat ultima oara pe un proiect Java. Si mai aveam Localstack pt AWS, parca si pt ala este o versiune in Testcontainers.