RSS

Spotkanie Koła Naukowego FullStack #4: Test Driven Development

Liczba odsłon: 90

Najpoważ­niej­szym wy­zwa­niem sto­ją­cym przed pro­gra­mis­tą jest spra­wie­nie, by na­pi­sa­ny kod po­praw­nie re­ali­zo­wał wy­ma­ga­nia klienta. Jedną z tech­nik, któ­re po­zwa­la­ją utrzy­mać w ry­zach ja­kość po­wsta­ją­ce­go opro­gra­mo­wa­nia jest TDD. Jej właś­nie zo­sta­ło po­świę­co­ne czwar­te spot­ka­nie Koła Naukowego Full­Stack, któ­re od­by­ło się 19 grud­nia na Wydziale Auto­ma­ty­ki, Elektro­ni­ki i Informa­ty­ki Politech­ni­ki Śląskiej w Gli­wi­cach. Poniższy tekst sta­no­wi re­la­cję z te­go wy­da­rze­nia.

Zasady tes­to­wa­nia opro­gra­mo­wa­nia zmie­nia­ły się na prze­strze­ni lat. Najpierw pro­gra­my by­ły te­sto­wa­ne głów­nie przez sa­mych twór­ców oraz użyt­kow­ni­ków koń­co­wych. Wraz z roz­wo­jem opro­gra­mo­wa­nia ko­mer­cyj­ne­go do pro­ce­su zo­sta­li włą­cze­ni pro­fe­sjo­nal­ni tes­te­rzy, któ­rych za­da­niem by­ło za­pew­nie­nie wy­so­kiej ja­koś­ci pro­gra­mów, choć­by by­ło to oku­pio­ne gnę­bie­niem pro­gra­mis­tów. W koń­cu, by zdjąć z tes­te­rów naj­bar­dziej mo­no­ton­ne za­da­nia i umoż­li­wić spraw­dza­nie w cią­gu pa­ru mi­nut, czy zna­ne wcześ­niej błę­dy nie po­wró­ci­ły wraz z no­wo wpro­wa­dzo­ny­mi zmia­na­mi, za­czę­to sto­so­wać te­sty auto­ma­tycz­ne.

Obecnie do te­stów auto­ma­tycz­nych za­li­cza­my jed­nost­ko­we, in­te­gra­cyj­ne, ca­ło­ścio­we (ang. end-to-end) oraz wie­le drob­niej­szych ka­te­gorii. Istnieje też wie­le me­to­do­lo­gii de­cy­du­ją­cych o tym, na ja­kim eta­pie roz­wo­ju wpro­wa­dzać te­sty auto­ma­tycz­ne i w ja­ki spo­sób je roz­wi­jać.

Jedną z cie­kaw­szych oraz mod­nych obec­nie tech­nik jest TDD, na­zy­wa­na też TFD. Zadania przed­sta­wie­nia jej szer­szej pub­licz­noś­ci pod­jął się Artur Dębski pod­czas czwar­te­go, ostat­nie­go w tym ro­ku spot­ka­nia Koła Naukowego Full­Stack, dzia­ła­ją­ce­go na Wydziale Auto­ma­ty­ki, Elektro­ni­ki i Informa­ty­ki Politech­ni­ki Śląskiej w Gli­wi­cach we współ­pra­cy z fir­mą Gorrion Software House. Jego pre­zen­tac­ja, za­ty­tu­ło­wa­na „Test Driven Develop­ment: jak te­sto­wać, że­by nie zwa­rio­wać (a przy­naj­mniej nie za bar­dzo)” mia­ła na ce­lu po­ka­za­nie za­let i wad tech­ni­ki oraz za­de­mon­stro­wa­nie jej uży­cia w prak­ty­ce.

Prelegent roz­po­czął od wy­jaś­nie­nia pod­staw tech­ni­ki. Cały pro­ces roz­wo­ju no­we­go ele­men­tu pro­gra­mu mu­si za­cząć się od za­pi­sa­nia te­stów do­ty­czą­cych pub­licz­ne­go API te­go mo­du­łu, za­nim jesz­cze po­wsta­nie choć je­den wiersz tek­stu źród­ło­we­go tes­to­wa­nej kla­sy lub me­to­dy. Bezpośred­nią ko­rzyś­cią z ta­kie­go po­dej­ścia jest obec­ność te­stów, któ­rych w przy­pad­ku wcześ­niej­sze­go im­ple­men­to­wa­nia funkcjo­nal­noś­ci nie chce się częs­to pi­sać. Chodzi jed­nak o du­żo wię­cej: za­pi­sa­nie te­stów, a więc ko­du wy­ko­rzy­stu­ją­ce­go no­wo two­rzo­ny mo­duł, przed im­ple­men­to­wa­niem API te­go mo­du­łu po­wo­du­je, że pro­gra­mis­ta jest zmu­szo­ny do prze­myś­le­nia, czy to API fak­tycz­nie speł­ni wy­ma­ga­nia użyt­kow­ni­ków. Jeżeli ko­rzy­sta­nie z mo­du­łu jest zbyt trud­ne, do­wie­my się o tym już pod­czas pi­sa­nia te­stów, a nie do­pie­ro przy pro­duk­cyj­nym wy­ko­rzys­ta­niu ko­du. Z te­go po­wo­du skrót „TDD” roz­wi­ja się cza­sem rów­nież ja­ko Test-Driven Design.

Ponadto, po­wsta­ją­ca im­ple­men­tac­ja bę­dzie od ra­zu na­pi­sa­na z myś­lą o moż­li­woś­ci sto­so­wa­nia te­stów auto­ma­tycz­nych. Dodawanie te­stów do ist­nie­ją­ce­go ko­du bar­dzo częs­to wią­że się z ko­niecz­noś­cią re­fak­to­ry­zo­wa­nia go i wy­ma­ga du­że­go (lub bar­dzo du­że­go) na­kła­du cza­su. Napisanie te­stów w pierw­szej ko­lej­noś­ci ozna­cza w ta­kim przy­pad­ku oszczęd­ność cza­su, gdyż two­rzo­na im­ple­men­tac­ja bę­dzie od ra­zu moż­li­wa do prze­te­sto­wa­nia.

Z obec­no­ści te­stów wy­ni­ka­ją oczy­wis­te ko­rzyś­ci. Najważniej­szą jest ochro­na przed re­gres­ją: je­że­li co­kol­wiek zmie­ni istot­ne z punk­tu wi­dze­nia pro­gra­mu za­cho­wa­nie te­sto­wa­ne­go ele­men­tu, mo­że­my wy­kryć i usu­nąć uster­kę za­nim tra­fi ona do klien­tów. W efek­cie wzras­ta po­ziom za­ufa­nia do ko­du, a pro­gra­miś­ci nie bo­ją się wpro­wa­dzać da­le­ko idą­cych zmian w im­ple­men­tac­ji. Testy sta­no­wią też naj­lep­szą moż­li­wą do­ku­men­tac­ję da­ne­go mo­du­łu funkcjo­nal­ne­go, gdyż sta­no­wią za­pis kon­trak­tów wej­ścio­wych i wyj­ścio­wych po­szcze­gól­nych pod­pro­gra­mów. Ostatecz­nie na­le­ży je za­tem uznać za ele­ment da­ją­cy zysk od­kła­da­ją­cy się w cza­sie: choć na po­cząt­ku bę­dzie­my uwa­żać, że pi­sa­nie te­stów zmniej­sza na­szą pro­duk­tyw­ność i wy­dłu­ża czas od po­mys­łu do pro­duk­tu, na eta­pie utrzy­ma­nia i re­fak­to­ry­za­cji ko­du spo­wo­du­ją one spo­rą oszczęd­ność cza­su, ner­wów, a być mo­że rów­nież pie­nię­dzy.

Testy auto­ma­tycz­ne po­win­ny być szyb­kie, aby pro­gra­mis­tów nie kor­ci­ło ich po­mi­ja­nie. Najwolniej­sze frag­men­ty ko­du, nie­istot­ne przy te­sto­wa­niu da­ne­go mo­du­łu funkcjo­nal­ne­go, po­win­ny być za­stę­po­wa­ne imi­tac­ja­mi (ang. mocks) tak, aby ope­rac­je trwa­ją­ce set­ki lub ty­sią­ce mili­se­kund rea­li­zo­wać w cią­gu po­je­dyn­czych mik­ro­se­kund. Stosując imi­ta­cje trze­ba jed­nak sta­le zwra­cać uwa­gę, by nie za­cząć te­sto­wać tych imi­ta­cji za­miast pro­duk­cyj­ne­go ko­du pro­gra­mu.

Podczas pi­sa­nia te­stów na­le­ży sku­pić się nie na im­ple­men­tac­ji, lecz na za­cho­wa­niu tes­to­wa­nych klas i pod­pro­gra­mów. Prelegent zde­cy­do­wa­nie sprze­ci­wił się wpro­wa­dza­niu do te­stów za­ło­żeń co do sta­nu obiek­tów, a tak­że od­ra­dził te­sto­wa­nie pry­wat­nych me­tod klas.

Poszczegól­ne te­sty po­win­ny skła­dać się z trzech częś­ci, wspól­nie reali­zu­ją­cych za­sa­dę „trzech A” (uważ­ni Czytelnicy mo­gą za­uwa­żyć po­do­bień­stwo te­go po­dzia­łu do given-when-then sto­so­wa­ne­go w tech­ni­ce BDDopi­sa­ne­go w re­lac­ji z ze­szło­rocz­nej kon­fe­ren­cji SpreadIT):

Ponadto, wszyst­kie te­sty po­win­ny speł­niać za­ło­że­nia F.I.R.S.T., opi­sa­ne przez Rober­ta Martina w książ­ce „Clean code”:

Jako źród­ło wie­dzy teo­re­tycz­nej i prak­tycz­nej pre­le­gent wy­mie­nił dwie książ­ki:

Druga część spot­ka­nia po­świę­co­na by­ła prak­tycz­nej pre­zen­tac­ji za­sto­so­wa­nia TDD. Prelegent po­sta­wił so­bie za za­da­nie za­pi­sa­nie w ję­zy­ku Java­Script me­to­dy greet() speł­nia­ją­cej wy­ma­ga­nia Greeting Kata. Kolejne, co­raz bar­dziej ab­strak­cyj­ne wy­ma­ga­nia opi­sa­ne w do­ku­men­cie pro­wa­dzi­ły do po­wsta­wa­nia ko­lej­nych te­stów, a bez­po­śred­nio po nich — do wpro­wa­dza­nia re­fak­to­ry­za­cji i roz­sze­rzeń ko­du. W pa­ru przy­pad­kach zmia­ny fak­tycz­nie psu­ły wcześ­niej dzia­ła­ją­cą funkcjo­nal­ność, jed­nak obec­ność te­stów po­zwa­la­ła szyb­ko to wy­kryć i wpro­wa­dzić od­po­wied­nie po­praw­ki.


Spotkanie zor­ga­ni­zo­wa­ne przez Koło Naukowe Full­Stack by­ło świet­ną okaz­ją, by oso­by nie za­zna­jo­mio­ne jesz­cze z tech­ni­ką te­stów jed­nost­ko­wych lub z jej za­sto­so­wa­niem w me­to­do­lo­gii TDD do­strzeg­ły war­tość do­da­wa­ną do pro­jek­tów in­for­ma­tycz­nych przez te­sty. Artur Dębski do­sko­na­le po­dzie­lił przy­zna­ny mu czas po­mię­dzy teorię i prak­ty­kę, naj­pierw słow­nie uza­sad­nia­jąc po­trze­bę tes­to­wa­nia w ogó­le, a za po­mo­cą TDD w szcze­gól­noś­ci, a póź­niej — de­mons­tru­jąc na ży­wo jak opi­sa­ne re­gu­ły sto­su­je się w co­dzien­nej pra­cy pro­gra­mis­ty. Szczególne bra­wa na­le­żą się mu za od­wa­gę zwią­za­ną z uży­wa­niem su­ge­ro­wa­ne­go na spot­ka­niach Koła Naukowego ję­zy­ka Java­Script, nie bę­dą­ce­go je­go pod­sta­wo­wym ję­zy­kiem.

Do wy­stą­pie­nia mia­łem je­dy­nie na­stę­pu­ją­ce, drob­ne uwa­gi, do­ty­czą­ce głów­nie bra­ku pew­nych in­for­mac­ji zwią­za­ne­go z ogra­ni­czo­nym cza­sem spot­ka­nia:

To, jak istot­nym te­ma­tem jest te­sto­wa­nie jed­nost­ko­we oraz tech­ni­ka TDD wi­dać po często­ści, z ja­ką po­ja­wia się on na wys­tą­pie­niach:

Przy okaz­ji, pro­gra­mis­tom Java chcą­cym na­uczyć się sto­so­wa­nia tech­ni­ki te­stów jed­nost­ko­wych, w tym zgod­nie z me­to­do­lo­gia­mi TDD/TFD oraz TAD, po­le­cam książ­kę Testowa­nie apli­kac­ji Java za po­mo­cą JUnit mo­je­go autor­stwa.