( Hevi | 2016. 12. 06., k – 00:41 )

"En egy szot sem irtam apirol, es a TDD-ben sincs szo arrol, hogy csak az APIt kell tesztelni..."

Szerintem csak mast ertetek API alatt: amirol saxus beszel, az az osztaly API-ja, interface-e, stb. Az osztaly publikus fuggvenyei absztaktabban nezve az osztaly API-ja.

En is csak mostansag jottem ra, hogy azert nem ertettem en se a TDD lenyeget eddig, mert nem ismerek meg olyan kenyelmes megoldast arra, hogy egy osztaly APIjanak a viselkedeset teszteljuk ahelyett, hogy az implementaciot ismetelnenk mockokkal. Alapvetoen ennek az egesznek egy blackbox tesztnek kellene lennie, ahol csak annyit fogalmazunk meg, hogy milyen bemeno parameterre milyen kijovo erteket szeretnenk kapni. Es itt jon be az, hogy a tesztek javitanak a kodminosegen, hiszen ha egyszeruen akarsz tesztet irni, akkor muszaj mellekhatasok nelkuli fuggvenyeket, SRP-t es egyebeket betartani.

Egy ideje gondolkodom mar, hogy a havesomerest mintajara meg kellene oldani, hogy a unit testek egyben mockokkent is hasznalhatok legyenek. Igy rogton tobb legyet utunk egy csapasra, hiszen ha irunk unit testeket az adott osztaly fuggvenyehez, akkor azt a viselkedesmintat, amit epp "dokumentaltunk" a tesztekben lehetne arra is hasznalni, hogy "visszajatszva" adott tipusu bemenetre adott kimenetet adjon.

Ezzel elernenk, hogy a tesztjeinket igazan "blackbox" tesztekkent tudjuk leirni, absztraktan csak a lenyeget, a "contractot", az API viselkedeset, mindegy hogy hivjuk. Es akkor nem kell foglalkozni az implementacios reszletekkel, a kod ugy es annyit valtozik amennyit nem szegyell. Ez a fajta teszteles igazan refaktoralas barat, hiszen a belso implementacio valtozasa nem hat ki a tesztekre, ha a logika nem valtozik (marpedig ez a lenyege a teszteknek), illetve, ha a mockolhatosagot is belevesszuk, akkor mivel A osztaly tesztjei B osztaly mockjai, ha A osztaly tesztjeinek valtoztatasa, eltori B osztaly tesztjeit, ezzel jelezve, hogy a refaktoralas soran mi mindent tortunk epp el.

Tehat szerintem az idealis teszt, aminek van is ertelme, es konnyen kabantarthato es ertheto, es mintha alapvetoen errol is szolt volna a dolog a kezdetekben, hogy csak, es kizarolag az elvart viselkedest teszteld, ne pedig azt, hogy milyen osztalyokat hiv meg a fuggvenyed milyen sorrendben. Tehat, pl ha van egy multiplyByTwo(Int a) fuggvenyed, akkor a teszted ne az legyen, hogy assertEquals(4, multiplyByTwo(2)), hanem, hogy


Int anyInt = Matcher.anyInt()
assertEquals(anyInt * 2, multiplyByTwo(anyInt))

Az elso esetben, mikor konkret ertekeket adtal meg, akkor nem vilagos, hogy azert ment at a teszt, mert tenyleg osszeszorozta a 2-t meg a 2-t, hiszen akar ossze is adhadta volna oket, az utobbi verzioval viszont nem csak, hogy sokkal tobb tesztesetet fedunk le a randomitassal, sokkal leirobbak is vagyunk, hiszen az egeszet egybeolvasva gyakorlatilag egy API dokumentacio. "Barmilyen Inttel meghivva a multiplyByTwo fuggveny, annak az Intnek a ketszereset kell visszaadja". Ez egy elengedhetetlen feltetele annak, hogy a tesztek mockokkent is tudjanak funkcionalni.

Es ezert nagyon fontos a megfelelo tipusok hasznalata, es nem belehanyni mindent Stringekbe. Minel szukebb tipusmeghatarozassal elunk, annal descriptivebb lesz a fuggvenyunk szignaturaja is, illetve a teszjeink is leirobbak es tomorebbek lesznek. Ami datum menjen valami Datum dipusba, ami megszamlalhato vegessegu halmaz, az legyen Enum, ami pozitiv egesz, az legyen valami pozitiv egesz tipus, stb. Es igy a kollegaidat is megkimeled attol, hogy utolag ujra ki kelljen deriteni pontosan milyen ertekek is haladhatnak el a kod adott reszleteben.