Értem, mindannyian. A tesztelés nem éppen a legszórakoztatóbb dolog, amit a fejlesztés során tehetünk.
De tudod, mi még kevésbé szórakoztató, mint a tesztelés?
Így van, hibakeresés.
Bárcsak lenne mód arra, hogy előre jelezzük a kódunkban előforduló hibákat, mielőtt azok ténylegesen megtörténnének. Ez annyi időt és stresszt takarít meg, amit életünk más részein is felhasználhatunk.
Itt jön be a tesztvezérelt fejlesztés, hogy megmentse a helyzetet. A tesztvezérelt fejlesztés vagy a TDD alapvetően a tesztek írása, mielőtt bármilyen kódot vagy függvényt írna a programba.
A TDD a gyakorlatban tulajdonképpen egy ciklus, ahol először megírjuk a sikertelen teszteket, majd megírjuk a minimális funkcionalitást ahhoz, hogy a teszt sikeres legyen, majd végül a programot úgy alakítjuk át, hogy a külső viselkedés megváltoztatása nélkül javítsuk azt.
Ezt a ciklust általában három szóval jelölik: PIROS, ZÖLD, REFAKTOR.
Beszéljük meg őket egyenként.
PIROS
A piros a TDD három ciklusa közül az első. Pirossal meg kell írnunk az aktuális funkció vagy lemaradás céljának eléréséhez szükséges teszteket.
Hadd hangsúlyozzam, hogy a tesztek akkor és csak akkor sikeresek, ha az aktuális lemaradáshoz szükséges összes felhasználói sztorit teljesítették. Ha a tesztek ezt megelőzően átmennek, akkor még nem értük el a kívánt célt vagy elfogadási kritériumokat az adott tulajdonsághoz.
Tehát hogyan írjunk jó teszteket, amelyek nemcsak a felhasználói történetet teljesítik, hanem tiszták és olvashatók is az Ön és mások számára, akik veled dolgoznak?
Szerencsére Bob Martin¹ Clean Codekönyvéből az F.I.R.S.T elve tesztelhető:
- Fast: A teszteknek elég gyorsaknak kell lenniük ahhoz, hogy ne tántorítsák el Önt vagy más programozókat a tesztelési eljárás inicializálásától.
- Ifüggetlen: Minden tesztnek függetlennek kell lennie, nem függhet a korábban megtörtént tesztektől. Ha a tesztekhez adatokra van szükség, akkor ezeket az adatokat a teszt legelején kell beállítani, nem az előzőnél.
- Imegismételhető: A teszteknek bármilyen környezetben futniuk kell, legyen az a helyi gépen, mások gépein vagy a git tárolóban. Ha a tesztek sikertelenek, az azért van, mert a program működése nem megfelelő, semmi más.
- Sönellenőrzés: A teszteknek meg kell mondaniuk, hogy sikeres-e vagy sem. Nem követelheti meg a fejlesztőktől, hogy manuálisan ellenőrizzék a teszt kimenetét annak eldöntése érdekében, hogy a teszt sikeres-e vagy sem. Ezt megtehetjük egy logikai érték visszaadásával, vagy a legtöbb programozási nyelvben vagy tesztkeretrendszerben elérhető assert módszer használatával.
- Timely: A teszteket közvetlenül azelőtt kell megírni, hogy a programozó megírja a kódot, hogy az sikeres legyen, és nem utána. Ez különösen igaz, ha követjük a TDD-t (ami vagyunk!).
A tesztek megírása után, megbizonyosodva arról, hogy az érvényesíti a felhasználói történetünk elfogadási feltételeit, valamint az F.I.R.S.T elvet követve, véglegesíthetjük kódunkat.
A véglegesítési üzenetnek [RED]-vel kell kezdődnie, jelezve, hogy a TDD-ciklusunk piros fázisában vagyunk, és a tesztek meghiúsulnak.
ZÖLD
Miután megírtuk tesztjeinket, és elhelyeztük a tárolónkban, most el kell kezdenünk a kódunk megvalósítását. Ebben a fázisban az a célunk, hogy minden olyan teszt sikeres legyen, amely korábban a piros szakaszon megbukott.
Jelenleg ez az egyetlen célunk, hogy megírjuk a minimális kódot, amely a tesztek sikeres teljesítéséhez és a felhasználói történet teljesítéséhez szükséges.
Ha végzett, véglegesítse a kódot a [ZÖLD] kezdetű véglegesítési üzenettel, jelezve, hogy az elvégzett teszteknek mostanra sikeresnek kell lenniük.
REFAKTOR
Miután megírtuk a kódot, itt a lehetőség az újrafaktorizálásra. Ne feledje, hogy a TDD ciklus Green lépésénél meg kell írnunk a minimális implementációt, hogy átmenjünk a Rednél írt teszteken.
Tehát mit tehetünk ebben a szakaszban? Nos, kezdhetjük azzal, hogy megszabadulunk a fel nem használt változóktól és importálástól, átnevezzük a tisztázatlan változókat és függvényeket, és megtisztítjuk a kódunkat.
De hogyan tisztítsuk meg a kódunkat?
Szerencsére van "itt" egy nagyszerű cikk, amelyet valóban az Ön által írt. Feltétlenül olvassa el ezt, miután elolvasta ezt!
Kód lefedettsége
A kódlefedettség annak mennyiségi mérése, hogy hány sor vagy kódblokk kerül végrehajtásra a tesztek futása közben. Más szavakkal, azt méri, hogy a kódból mennyit teszteltek.
A kódlefedettség mérése percentilis formátumban történik, 0 és 100% között. Ez azt jelenti, hogy ha a kód lefedettsége 85%, akkor a kód 15%-a még nem tesztelt.
A cél mindig a 100%-os elérése legyen, bár a valóság ettől eltérhet. A 100%-os kódlefedettség elérése biztosítja, hogy a kód jól tesztelt és hibamentes legyen.
TDD akcióban
Most, hogy félretettük az elméletet, vessünk egy pillantást a TDD megvalósítására projektünkben.
Itt egy fejlesztő (én!) ír egy tesztet egy új funkcióhoz a TDD ciklus Red szakaszában. A pipeline ekkor (meglepetés, meglepetés) meghiúsul, mert még nincs megírva a tesztek megvalósítása.
Ezután a fejlesztő kódolja a funkció megvalósítását, és a folyamat átmegy. Ennek az az oka, hogy az előző commitban írt tesztek sikeresek voltak, ami azt jelzi, hogy a felhasználói történet teljesítésének célja megvalósult.
Végül ez a fejlesztő úgy alakítja át az alkalmazást, hogy megszabadul a kódtól, amelyet végül nem használtak.
Következtetés
A TDD tökéletes?
No.
Egyes fejlesztők számára a tesztek készítése egyszerűen nem élvezetes. Néha a fejlesztők is küszködnek a tesztelési könyvtárral, és kitalálják, hogy a tesztelési könyvtár melyik kódja milyen funkcionalitást tesztel.
De vajon ez a legjobb gyakorlat?
Egyelőre igen. Különösen, ha meg akarjuk őrizni alkalmazásunk kódjának minőségét. A kódunk tesztelése a funkcionalitás megvalósítása előtt biztosítja, hogy a kód mentes legyen a hibáktól, és megfelel a hátralék elfogadási feltételeinek.
[1]: Tiszta kód, Bob Martin