Miért írjunk automatizált teszteseteket?

2010. április 15-én az Agilis Szoftverfejlesztők Egyesületének szervezésében az egyik aktuális téma Fejes Péter előadásában a teszt alapú fejlesztés (Test Driven Development, röviden TDD) bemutatása volt. Sajnos, jó magam nem tudtam részt venni a rendezvényen, kollégáim számoltak be az elhangzottakról.

Ismereteim szerint kb. 35-en voltak jelen, a résztvevők túlnyomó többsége projektmenedzser volt. A beszélgetés egy része az ő meggyőzésükről szólt: miért legyen része az automatizált tesztek készítése a szoftverfejlesztési folyamatnak? Úgy gondoltam, érdemes körbejárnom nekem is a témát, és támogatnom a téma evangelistáit.

Az automatizált tesztek írásával kapcsolatos legalapvetőbb kérdés a következő:

Megéri-e a befektetett idő?

Ha felülethez készítünk automatikusan klikkelő robotokat, az véleményem szerint az esetek többségében nagyon durván nem éri meg (erről most nem írok részletesen). Viszont más a helyzet akkor, ha tesztkódot (unit és integrációs teszteket) írunk, amely ugyanúgy a kódbázis részét alkotja, mint a produkciós kód.

Miért jó?

Nagyon sokan félreértik a TDD lényegét. A TDD lényege nem az, hogy a rengeteg unit teszt garantálja a program hibamentességét. A TDD első számú feladata az, hogy jó minőségű (értsd: könnyen olvasható, karbantartható kód készüljön). Azáltal, hogy unit teszteket írunk a produkciós kódhoz, olyan produkciós kód készül el, amely tesztelhető is, merthogy
úgy kell megírnunk a produkciós kódunkat, hogy az unit tesztelhető legyen. Ennek közvetlen eredménye:

  • Nem keverednek össze az alkalmazás különböző rétegei (prezentációs réteg, szolgáltatás rétegek, üzleti logika, adatelérési logika).
  • Lazán csatolt osztályokat eredményez. Az osztályok közötti kapcsolatok oldódnak.
  • Kikényszeríti a Single Responsibility Principle elvet. (Azaz egy osztálynak csak egy felelőssége legyen. Erről is írok egy későbbi post-ban.) Ezzel elkerülhetőek a gigaméretű, nehezen átlátható és karbantartható osztályok.

Ha vannak automatizált tesztjeink (és most már nem csak a unit tesztekre gondolok), annak van egy kellemes mellékhatása: ha változtatunk a kódon, és hiba kerül a szoftverbe, akkor jó eséllyel kimutatják, hogy porszem került a gépezetbe. Ami viszont még ennél is fontosabb: merünk belenyúlni a kódba, hogy refaktorálhassuk. Tehát a kód minőségén folyamatosan tudunk javítani. Mert ugye először mindig egyszerű megoldásokat készítünk, aztán valahogy mindig bonyolódik a helyzet, és egy idő után összegányolódik a kód (ez egy természetes folyamat). Ha merünk refaktorálni, akkor ez folyamatosan korrigálható.

A jó minőségű kódban kevesebb lesz a hiba, mert sokkal egyszerűbb és ezáltal átláthatóbb a kód. Ha pedig mégis találunk hibát, azt hamarabb javítjuk ki.

Megjegyzem még, az egész agilis szoftverfejlesztésnek az alapja az, hogy ha megváltozik egy igény, vagy éppen egy új jön, akkor nem kell aggódnunk amiatt, hogy a kódunkat módosítsuk. A tesztek nagy valószínűséggel jelzik, ha valahol hibát vétünk.

Tehát megéri-e?

A Toyota híres az általa kitalált Lean gyártási technológiáról, amelyről közismert, hogy sokkal hatékonyabb működést eredményez, mint az a típusú tömeggyártás, amit a többi autógyár képvisel. Amikor megkérdezték a Toyota szakemberét, hogy mennyivel hatékonyabb az ő módszerük a többieknél, ők kb. ezt válaszolták:

Nem érdekel. Mi így dolgozunk.

Tudták az azóta általánosan is elfogadott és híressé vált lean módszerükről, hogy sokkal jobb, mint a többi autógyár hagyományos termelési folyamata, mert a folyamatos megfigyelésen, kiértékelésen és a folyamataik folyamatos, azonnali javításán alapult. A céljuk a felesleges munkának, a selejtnek és a pocséklásnak (angolul összefoglalóan: waste-nek) a minimalizálása volt. Ez a vállalati kultúrájuk része, és mint olyan, nem kell győzködni a cégen belül senkit, hogy így dolgozzon, mert ez természetes.

Igen, az automatizált tesztek megírása és karbantartása időt visz el. És sokkal több időt takarít meg.

Ne felejtsük el, hogy a szoftver életciklusa nem akkor ér véget, amikor a szoftvert átadják, és a projektet lezárják (ez minden projektmenedzser célja, nem?). A problémák akkor kezdődnek, amikor ténylegesen elkezdik használni a szoftvert. Rájönnek, hogy még kellenek bele dolgok, valamit meg kell változtatni, és még hibákat is jelentenek. Lehet, hogy később még szeretnék továbbfejlesztetni is. (Az nekünk egy újabb lehetőség, nem?)

A mi csapatunkban 5 fejlesztő dolgozik. Viszont elég volt egyetlen tesztelő. A szoftver eléggé összetett, 3 éve fejlesztjük. Irtózatosan nagy a kódbázis. Kérdés: abban az esetben, ha nem írunk automatizált teszteket, hány tesztelő kell, hogy egy 3 évig folyamatosan fejlesztett rendszerben történő változtatás után állandóan újrateszteljük az alkalmazást? Ja, és persze úgy, hogy ne heteket kelljen várni a teszt eredményére. Nem tudom, és nem is érdekel, abban viszont biztos vagyok, hogy 1 tesztelő nem lenne elég. És még egy érdekes kérdés: ezek a képzeletbeli tesztelők mi alapján tesztelnének? Csakis előre megírt forgatókönyvek alapján tudnának, amiket lehet doksizni és állandóan frissítgetni. Ha jó automatizált tesztjeink vannak, kevés tesztelő kell, és az ő feladatuk átcsap felfedező típusú teszteléssé. Nem kell szisztematikusan ellenőrizni több száz vagy ezer forgatókönyvet, mert a lehetséges elágazásokat már leteszteltük.

Mindig van idő

Ha ez még nem elég, és még mindig azt mondja valaki, hogy mindig nagy a rohanás, és nincs idő a unit tesztekkel kapcsolatos bíbelődésre, azoknak ajánlom a “Mindig van idő” c. írásomat.

Vak vezet világtalant

Hallottam, hogy az előadáson az egyik résztvevő a következőt mondta:

Kipróbáltuk a TDD-t, és azt láttuk, hogy nem működik. Nincs idő a tesztek megírására és karbantartására.

Pontosan ugyanezt hallom vissza máshonnan is, és azt is tudom, hogy valójában miért mondják ezt:

  • Sok esetben a csapat, amiben valaki a TDD kultúráját akarja meghonosítani, diszfunkcionális. A csapat nem csapatként dolgozik, ahol közösen beszélik meg és probálják ki az új ötleteket. Valaki le akarja nyomni a többiek torkán, hogy legyen TDD. A fejlesztők nagy része ezt pedig úgy éli meg, hogy meg akarják változtatni, ahogyan ő dolgozik. Szkeptikusan és negatívan állnak hozzá direkt a dologhoz. Nem azt nézi, hogy hogyan lehetne használni ezt az eszközt, hanem a kifogásokat keresi. Ezért nem tartom jó ötletnek azt sem, hogy a projektmenedzserek írják elő a csapatoknak a TDD alkalmazását. A fejlesztőknek maguknak kell rájönniük, hogy ez jó, és hogy hogyan csinálják, hogyan építsék be a saját folyamataikba.
  • Sokféleképpen lehet rosszul csinálni a TDD-t és az automatizált tesztek írását. Ha rosszul csinálják, akkor rossz is lesz az eredmény. Ez például abban mutatkozhat meg, hogy tényleg sokáig tart a tesztek megírása. Példa: egy osztálynak csak a publikus metódusain keresztül ellenőrizzünk! Ha próbálnánk a mélyét is tesztelni az osztálynak, akkor a tesztkódunk túlságosan támaszkodna a konkrét produkciós kódhoz, ami egyrészt bonyolult teszteket eredményezne, másrészt pedig a produkciós kód kismértékű változtatása is változtatást eredményezne a tesztkódban is. Egy másik példa: ha a unit tesztek helyett inkább integrációs teszteket írunk, azok futása sokáig fog tartani, és jelentősen csökkenti a fejlesztő produktivitását. Nem mindegy, hogy egy teszt 0.1mp-ig vagy 30mp-ig fut. Viszont az integrációs tesztekre is szükség van!
  • Az egységsugarú programozó nem látja át az automatizált tesztek – fent említett – középtávú hatását és eredményét.

Nagyon fontos, hogy türelemmel legyünk. Egy csapatnak meg kell tanulnia, hogy hogyan kell unit tesztelni: mikor írjanak integrációs tesztet, mikor írjanak unit tesztet, mikor mockoljanak, hogyan jönnek létre a tesztadataik a teszteléshez, mi a tesztelő feladata, tehát: összességében hogyan biztosítsák hatékonyan azt, hogy az alkalmazásban a hibák előfordulásának kockázata és a hibák előfordulásából fakadó hibajavítási munkáknak a ráfordítása minimális legyen. Igen, ez nem triviális. Ha kell, vegyünk fel egy olyan fejlesztőt, akiről tudjuk, hogy behozza ezt a tudást a csapatba. Előnyben vannak azok a csapatok, akik hosszabb kifutású projektekben dolgoznak. Több lehetőségük van a próbálkozásra, mint azoknak, akiknek másfél hónap alatt kell egy rendszert üzembe helyezni.

Ez sem csodafegyver

Tisztában kell lennünk azzal, hogy semmilyen tesztelésen alapuló módszer nem ad 100%-os eredményt. Ez igaz a TDD-re is.

A közúti közlekedési balesetekhez tudnám hasonlítani az egész tesztelés témakörét.

Sok haláleset történt az elmúlt években az utakon. Magyarországnak vállalást kellett tennie az EU felé, hogy csökkenti az egy évben közúti balesetben elhunytak számát. Erre pedig megvannak a megfelelő módszerek. Sebességkorlátozások, balesetveszélyre figyelmeztető táblák, autópályákon akusztikus felfestés a leállósávnál, gyakoribb ellenőrzések (traffipax, alkohol), komolyabb büntetések, objektív felelősség stb. Nem lehet 0-ra csökkenteni a balesetek számát, de lehet törekedni a minimalizásukra.

Ennek kell eszünkbe jutnia akkor is, amikor kísérletezünk az automatizált tesztekkel. Milyen hatással jár, ha valamit konkrétan így vagy úgy csinálunk? Megéri-e? Lehet-e jobban csinálni?

Vannak persze ajánlások, meg mindenféle praktikák, mert ez nem egy egzakt tudomány. Pl. az előadáson is elhangzott a 80%-os kódlefedettség. (Merthogy a 100%-os lefedettség ártalmas. Ennek az elérése és fenntartása aránytalanul sok ráfordítást igényel.)

Záró gondolat

Nekem furcsa az, hogy projektmenedzsereket és vezetőket kell győzködni arról, hogy legyenek-e automatizált tesztek vagy sem. Ők nem tudnak programozni, és nem ők azok, akik fejlesztik a szoftvert.

Az automatizált tesztek elkészítése része a mindennapi szoftverfejlesztési feladatnak. Ezt kell megérteni. Ez nem projektmenedzsment döntés, ez szakmai döntés. Ezt minden fejlesztőnek tudnia kell. Mint ahogy azt is tudjuk, hogy nem mondjuk a kódunkra azt, hogy kész, ha egyszer legalább ki nem próbáltuk.

Ugyanez van a tesztekkel is. Csinálni kell őket. Minden fejlesztőnek.

Share
This entry was posted in Módszertan, Programozás and tagged , , , . Bookmark the permalink. Follow any comments here with the RSS feed for this post. Trackbacks are closed, but you can post a comment.

Comments

  • mindennel egyetértek, kívéve hogy ‘a fejlesztőknek maguknak kell rájönni h ez jó és hogyan használják’ – szerintem ehhez leggyakrabban külső segítség kell (nyilván nem PM oldalról, nem is lenne hiteles). Máig emlékszem ~7 éve a legtehetségesebb (akkor még) juniorom összes ‘miért nem működik’ jellegű kérdését visszadobáltam és nem voltam hajlandó ránézni se amíg nem írt rá tesztet amivel bemutatta hogy mi nem működik. Ez a módszer pl fényesen bevált :) de nehezebb eseteknél pár óra pair programming is teljesen működik. Aki meg fejlesztő létére komolyan állítja hogy a tesztírás lassítja a munkát az nem tud tesztet írni, meg kell tanítani (helyesen) csinálni..

  • Marhefka István

    Apr 17th, 2010

    Kristóf: Jó módszer :) Egyetértek, hogy a leghatékonyabb a külső segítség felhasználása.

    “A fejlesztőknek maguknak kell rájönni h ez jó és hogyan használják” – szerintem ez mindenképpen elegendhetetlen. Ennek egyik módszere, hogy coacholod az illetőt (ahogy Te írtad).

    Pár óra alatt szerintem csak az alapokat lehet elsajátítani. _Jó_ tesztkészlet megírása viszont igenis nem egyszerű. Azt tekintem jónak, amit később nem lesz fájdalmas karbantartani, olvasni.

    A teszteket is valamilyen módon rendszerezni kell, hogy kezelhetőek legyenek. Ennek egyik alapja, hogy ugyanaz legyen a package-struktúra, mint a produkciós kód esetében, a másik az, hogy alap esetben egy-egy tesztosztály egy osztálynak a teszteléséért feleljen. Ha túl nagy egy tesztosztály, akkor meg célszerű szétválasztani valamilyen szempontok alapján többe. (Bár kérdés, hogy ilyenkor nem az eredeti produkciós kóddal van-e a gond.) Hogyan nevezzem el a tesztmetódusaimat?

    Azért az sem triviális szerintem, hogy hogyan érjük el azt, hogy az osztályaink tesztelhetőek legyenek, és hogy érvényesítsük a Single Responsibility Principle-t.

  • “Unit tesztek nélkül fejleszteni olyan, mint amikor valaki sötétben festi újra ki a lakását” (Vrg)

  • Marhefka István

    Apr 17th, 2010

    Találó a hasonlat. Ha nincsenek automatizált tesztjeink, az gyakorlatilag azt jelenti, h állandóan attól tarthatunk, h hibás a szoftver.

  • Érdekes, hogy az automatizált teszt eseteknél ritkábban esik szó arról, hogy azért is jó unit tesztek használata, mert rövidül a fejlesztési ciklus.
    Amíg nem volt ilyen, ha valamit változtatni kellett, be kellett lökni az alkalmazásszervert (pl. egy WebSphere Portal egy kis erőforrású gépen nem 1-2 mp alatt indul el), és a GUI felületen elvergődni a fejlesztendő funkcióig, és kipróbálni. Klikkeltünk, nem sikerült, kezdhettük az egészet előröl. Az okosabbak az osztályokba szétszórva írtak main() függvényeket, amivel letesztelték az adott kódot.
    Most, amiket oktatásokat tartok, azt szoktam prezentálni, hogy a DAO és business réteget meg tudod írni anélkül, hogy azt a fránya alkalmazásszervert elindítanád. Ok, vannak, amik gyorsan indulnak, és a világ is e felé megy (GF 3), ahol egy deploy 5 mp is lehet, de azért vannak lassú környezetek, és ha egy deploy 30 mp, az is sok. És ha minden tök jó, elé teszel egy irtó vékony GUI réteget, és élvezed a kitesztelt alsó réteged minden előnyét.
    Szóval ha már elindulsz azon az úton, hogy a main-jeidet kiváltod teszt esetekkel, ott már úgyis elindulsz a lejtőn.
    És itt a motiváció nem is a kiteszteltség, hanem a nagyon gyors fejlesztési ciklus.

  • Marhefka István

    Apr 18th, 2010

    István: Áááá… Ez az aspektus kimaradt a cikkből :)

  • Saját tapasztalatom az, hogy elegendő egyetlen embernek írnia. Mert ha lassan rendbeszedi azon részeket, amiken dogozik akkor kialakít egy jó kis halom automatizált tesztet. Ez lassan elér egy kritikus tömeget. Ezen kritikus tömeg elérése után más fejlesztők is elkezdik használni az előnyeit egy kész testhalmaznak (fent már említetted – mernek változtatni, el tudják kerülni, hogy homok kerüljün a gépezetbe) És innen már csak egy lépés, hogy ők is elkezdik írni. Magyarán nincs veszve semmi, csak el kell kezdeni, akár legalulról is.

    Saját tapasztalatom szerint a code coverage nem olyan lényeges, mérőszám. Azt vettem észre hogy 25-30%-os coverage esetén már észrevehető hatékonyságnövelést okoz az automatizált tesztek használata. Már azzal is rengeteget nyersz, ha biztos vagy abban, hogy a kulcsfolyamataid rendesen működnek.

  • Marhefka István

    Oct 5th, 2010

    “Saját tapasztalatom az, hogy elegendő egyetlen embernek írnia.”

    Lehet, hogy induláshoz (kedvcsinálónak) elég, de hamar ki kell alakulnia annak, hogy nem egy ember feladata és felelőssége, hogy rendben tartsa a teszt kódbázist. Ráadásul a TDD lényege, hogy előbb írunk teszt-kódot, mint production kódot. Amúgy ha van egy jó vezető fejlesztő, akkor standardként előírja a csapat többi tagja számára a teszteket készítését, és innentől kezdve már nem lehet több kérdés..

    “Saját tapasztalatom szerint a code coverage nem olyan lényeges, mérőszám. Azt vettem észre hogy 25-30%-os coverage esetén már észrevehető hatékonyságnövelést okoz az automatizált tesztek használata. Már azzal is rengeteget nyersz, ha biztos vagy abban, hogy a kulcsfolyamataid rendesen működnek.”

    Egyetértek. Te onnan közelíted meg, hogy már ez is jobb, mint a semmi, én viszont itt nem állok meg: az alkalmazást “megfelelően” kell lefedni. Ez azt jelenti, hogy meg kell találni a megfelelő arányokat minden projekt esetében, hogy megfelelően csökkenthessük a módosítások okozta kockázatokat.

Leave a Comment