A számítógép-programozásban az egységtesztelés a szoftvertesztelésnek egy olyan módszere, amelynek során a forráskód egységeit (egy vagy több számítógépes program modul készletet) a kapcsolódó vezérlő adatokkal, a felhasználási-és a működtető eljárásokkal együtt tesztelik annak meghatározására, hogy azok elérik-e kitűzött céljukat.
Leírás Az egységtesztek általában automatizált tesztek, amelyeket a szoftverfejlesztők írnak és futtatnak annak biztosítása érdekében, hogy egy adott alkalmazás adott szakasza (úgynevezett "egység") megfeleljen a kialakításának és az elvárt, kívánt módon viselkedjen. A procedurális programozásban az egység lehet egy teljes modul, de túlnyomórészt egyéni függvény vagy eljárás. Objektumorientált programozás során az egység gyakran egy teljes felület/interfész, például egy osztály, de lehet egyedi metódus is. Azáltal, hogyha a tesztek készítése először a legkisebb tesztelhető egységekre történik, majd az azok közötti összetett, egész viselkedésre, átfogó tesztek hozhatók létre komplex alkalmazásokhoz.
Az esetlegesen felmerülő problémák elkülönítése érdekében minden tesztesetet egymástól függetlenül, külön kell tesztelni. Egy modul izolált tesztelésének elősegítésére többek között az összetett, bonyolult viselkedéssel bíró objektumokat helyettesítő, ún. álcaobjektumok (mock objektumok) is használatosak.
A fejlesztés során a szoftverfejlesztő kritériumokat vagy ismert eredményeket is kódolhat a tesztbe azért, hogy ellenőrizze az adott egység helyességét. Az egyes tesztesetek végrehajtása során a keretrendszerek naplózzák azokat a teszteket, amelyek bármely kritériumot nem teljesítik és jelentést készítenek róluk egy összefoglalóban.
Az egységtesztek írása és fenntartása gyorsabban kivitelezhető, mint a paraméterezett tesztek esetén. Ezek lehetővé teszik egy teszt többszöri végrehajtását különböző bemeneti halmazokkal, csökkentve ezzel a tesztkód reprodukálását. A hagyományos egységtesztektől eltérően, amik általában zárt metódusok és invariáns körülményeket tesztelnek, a paraméterezett tesztek bármilyen paraméterkészletet felvehetnek. A paraméterezett teszteket támogatja a TestNG, JUnit és ennek .NET-beli párja, az xUnit is. Az egységteszthez tartozó megfelelő paraméterek manuálisan is megadhatók, vagy egyes esetekben a teszt keretrendszere automatikusan generálja őket. Az utóbbi években támogatták az erőteljesebb (egység) tesztek írását, az elméletek koncepciójának kiaknázását, az olyan teszteseteket, amelyek ugyanazokat a lépéseket hajtják végre, de futási idő alatt generált tesztadatokat használnak, ellentétben a szokásos paraméterezett teszttel, amely ugyanazokat a végrehajtási lépéseket használja olyan bemeneti halmazokkal, amelyek előre definiáltak.
Előnyök Az egységteszt célja a program minden részének elkülönítése és annak igazolása, hogy az egyes részek helyesek. Az egységteszt szigorú, írásbeli „szerződést” biztosít, amelyet az adott kóddarabnak ki kell elégítenie. Ennek eredményeként az egységteszt számos előnyt kínál.
Az egységtesztelés a szoftverfejlesztési folyamat korai szakaszában találja meg a problémákat. Ez egyaránt magában foglalja mind a programozó végrehajtásának bugjait, mind az egység specifikációjának hibáit vagy annak hiányzó részeit. Az alapos, részletes tesztkészlet megírásának folyamata arra készteti a fejlesztőt, hogy gondolja át a bemeneteket, a kimeneteket és a hibafeltételeket, ezáltal így pontosabban meghatározza az egység kívánt viselkedését. A hibakeresés költsége a kódolás megkezdése vagy a kód első írása előtt jóval alacsonyabb, mint a hiba későbbi felismerésének, azonosításának és kijavításának költsége. A kiadott kódban szereplő hibák szintén költséges problémákat okozhatnak a szoftver végfelhasználói számára. A kódra lehetetlen vagy bonyolult egységtesztet írni, ha az rosszul van megírva, így az egységtesztelés arra készteti a fejlesztőket, hogy jobban strukturálják a funkciókat és az objektumokat.
A tesztvezérelt fejlesztés során (TDD), amelyet gyakran használnak mind az extrém programozásban, mind a scrumban, az egységteszteket a kód írása előtt hozzák létre. Amikor a tesztek sikeresek, az adott kódot teljesnek tekintik. Ha az egységteszt sikertelen, akkor bugnak tekintik vagy a megváltozott kódot, vagy magukat a teszteket is. Ezután az egységtesztek lehetővé teszik a hiba vagy a balsiker helyének könnyű nyomon követését. Mivel az egységtesztek azelőtt figyelmeztetik fejlesztői csoportot a problémára, mielőtt a kódot azt kiosztanák a tesztelőknek vagy az ügyfeleknek, a lehetséges problémák a fejlesztési folyamat korai szakaszában fedezhetők fel.
Az egységteszt lehetővé teszi a programozó számára a kódrefaktorálást vagy a rendszerkönyvtárak egy későbbi időpontban történő frissítését és a modul továbbra is helyes működéséről való megbizonyosodást(például regressziós teszteléskor). A művelet során tesztesetek írására kerül sor minden függvényre és metódusra azért, hogyha bármilyen változás hibát okozna, az gyorsan azonosítható legyen. Az egységtesztek olyan változásokat észlelnek és derítenek ki, amelyek megsérthetik a szerződésalapú tervezést.
Az egységtesztelés csökkentheti a bizonytalanságot magukban az egységekben és a lentről felfelé irányuló tesztelési stílusú (bottom-up testing style) megközelítésben használható. Ha először teszteljük a program részeit, majd teszteljük a részek összegét, az integrációs tesztelés sokkal könnyebbé válik.
Az egységtesztelés a rendszer egyfajta élő dokumentációját nyújtja. Azok a fejlesztők, akik meg akarják tanulni, hogy egy egység milyen funkcionalitást kínál és hogy hogyan kell ezt használni, megnézhetik az egységtesztelést, hogy alapvetően megismerjék az egység felületét (API).
Az egységteszt olyan jellemzőket testesít meg, amelyek kritikusak az egység sikeréhez. Ezek a jellemzők jelezhetik az egység megfelelő / nem megfelelő használatát, továbbá a negatív viselkedéseket, amelyek az egység által „csapdába” (trapped by the unit) estek. Egy egységteszt önmagában dokumentálja ezeket a kritikus jellemzőket, habár sok szoftverfejlesztő környezet nem kizárólag arra szolgál, hogy a fejlesztés alatt álló terméket dokumentálja a kód alapján.
Ha a szoftvert a tesztvezérelt megközelítés alkalmazásával fejlesztik, interfész meghatározására történő egységtesztek, valamint a teszt letelte után elvégzett kódrefaktorálási tevékenységek kombinációja léphet a hivatalos tervezés helyébe. Minden egységteszt tervezési elemnek tekinthető, amely meghatározza az osztályokat, metódusokat és a megfigyelhető viselkedést.
Korlátok és hátrányok A tesztelés nem fog minden hibát elfogni a programban, mert nem tud minden végrehajtási útvonalat kiértékelni. Ez a döntési probléma (a megállási probléma (halting problem) szülő-halmaza, azaz, hogy az adott program befejezi-e a futását vagy örökre fut,) eldönthetetlen, mivel lehetnek olyan kódrészek, amikre nem írható olyan ellenőrző algoritmus, amely igaz-hamis válasszal szolgálna a kimenetében. Ugyanez igaz az egységtesztelésre is. Ezenkívül, a definíció szerint az egységteszt csak maguknak az egységeknek a funkcionalitását teszteli. Ebből kifolyólag nem fog elkapni integrációs hibákat vagy tágabb, rendszerszintű hibákat (például több egységen végrehajtott függvények vagy nem funkcionális tesztterületek, például a teljesítmény) Az egységtesztelést más szoftvertesztelési tevékenységekkel összefüggésben kell elvégezni, mivel ezek csak bizonyos hibák jelenlétét vagy hiányát mutatják; nem tudják bizonyítani az összes hiba hiányát. Ahhoz, hogy garantálva legyen a végrehajtás minden útjának és minden lehetséges bemenetnek a helyes viselkedése és biztosítva legyen a hibák hiánya, más technikákra is szükség van, nevezetesen formális módszerek alkalmazására annak igazolására, hogy a szoftverkomponensnek nincs váratlan viselkedése.
Egy egységtesztek alkotta bonyolult hierarchia nem egyezik meg az integrációs teszteléssel. A periferikus egységekkel való integráció bele kell, hogy tartozzon az integrációs tesztekbe, de az egységtesztekbe nem. Az integrációs tesztelés még mindig erősen támaszkodik az emberek általi manuális tesztelésre; ugyanis a magasszintű vagy globális körű tesztelést nehéz automatizálni, a manuális tesztelés gyakran könnyebben és olcsóbban kivitelezhetőbbnek tűnik.
A szoftvertesztelés egy kombinatorikus probléma. Például, minden Boolean döntési nyilatkozatnak legalább két tesztre van szüksége: egy „igaz” és egy „hamis” kimenetelűre. Ennek eredményeként a programozóknak minden megírt kódsorra 3–5 sornyi tesztkódra van szükségük. Ez nyilvánvalóan időt vesz igénybe és esetleg nem éri meg a belefektetett erőfeszítést. Vannak olyan problémák, amelyeket egyáltalán nem könnyű tesztelni - például nemdeterminisztikus algoritmusok, vagy több szálat tartalmaznak. Továbbá, az egységteszt kódja valószínűleg legalább olyannyira hibás (buggy), mint az a kód, amit tesztelni hivatott. Fred Brooks a The Mythical Man Month című könyvében eképpen szól „Soha ne menj a tengerre két kronométerrel; vigyél egyet vagy hármat.” Ugyanis, ha két kronométer ellentmond, akkor honnan tudod, hogy melyik a helyes?
Az egységteszt írásával kapcsolatos másik kihívás a reális és hasznos tesztek felállításának nehézsége. Szükséges megteremteni a megfelelő kezdeti, kiinduló feltételeket azért, hogy az alkalmazás tesztelt része a teljes rendszer részeként viselkedjen. Ha ezek a kezdeti feltételek nincsenek helyesen beállítva, akkor a teszt nem gyakorolja a kódot reális kontextusban, ami csökkenti az egységteszt értékét és pontosságát.
Az egységtesztelés előnyeinek eléréséhez szigorú fegyelemre van szükség az egész szoftverfejlesztési folyamat során. Alapvető fontosságú, hogy ne csak az elvégzett teszteket, hanem a szoftver ezen vagy bármely más egységének forráskódjában elvégzett változtatásokat is gondosan rögzítsük. Lényeges egy verziókezelő rendszer használata is. Ha az egység egy későbbi verziója elbukik egy olyan sajátos teszten, amin korábban átment, akkor a verziókezelő szoftver a forráskód-változtatások (ha vannak) listáját képes nyújtani, amelyek az egységre azóta vonatkoznak.
Szintén elengedhetetlen egy fenntartható folyamat implementálása annak biztosítása érdekében, hogy a kudarcba fulladt tesztesetek rendszeresen felülvizsgálva és azonnal kezelve legyenek. Ha egy ilyen folyamatot nem valósítanak meg és berögzülnek a csapat munkafolyamatába, akkor az alkalmazás nem fog szinkronban fejlődni az egységtesztkészletével, ezzel növelve a hamis pozitív eredményeket és csökkentve a tesztkészlet hatékonyságát.
A beágyazott rendszerek szoftverének egységtesztelése egy egyedülálló kihívást jelent: mivel a szoftvert egy, a futási felületétől eltérő platformon fejlesztették ki, a tesztprogramot az aktuális telepítési környezetben nem lehet könnyedén futtatni, mint ahogy ez az asztali programok esetében lehetséges.
Az egységtesztek általában akkor a legkönnyebbek, ha a metódusnak néhány paramétere és valamilyen kimenete van. Nem ilyen könnyű egységteszteket készíteni, amikor a metódus fő függvénye kölcsönhatásban van valamilyen alkalmazáson kívüli dologgal. Például egy adatbázissal dolgozó metódusnál szükség lehet egy replikára (mock up) az adatbázis-interakciók modelljének létrehozására, amely valószínűleg nem lesz olyan átfogó és széleskörű, mint a valódi adatbázis-interakciók.
Példa