Az egységtesztelés , néha az egységtesztelés vagy az egységtesztelés ( angol. unit testing ) egy olyan programozási folyamat, amely lehetővé teszi a programforráskód egyes moduljainak, egy vagy több programmodul készleteinek helyességének ellenőrzését a megfelelő vezérlőadatokkal együtt, felhasználási és feldolgozási eljárások.
Az ötlet az, hogy minden nem triviális függvényhez vagy módszerhez teszteket írjunk. Ezzel gyorsan ellenőrizhető, hogy a következő kódmódosítás nem vezetett-e regresszióhoz , azaz hibák megjelenéséhez a program már tesztelt helyein, valamint megkönnyíti az ilyen hibák észlelését és kiküszöbölését. Például bármikor frissítheti a projektben használt könyvtárat az aktuális verzióra tesztek futtatásával és az összeférhetetlenségek azonosításával.
Az egységteszt célja a program egyes részeinek elkülönítése, és megmutatni, hogy ezek a részek külön-külön is működnek.
Az ilyen típusú tesztelést általában programozók végzik .
Az egységteszt később lehetővé teszi a programozók számára, hogy újrafaktoráljanak , miközben biztosak lehetnek abban, hogy az egység továbbra is megfelelően működik ( regressziós tesztelés ). Ez arra ösztönzi a programozókat, hogy módosítsák a kódot, mivel elég egyszerű ellenőrizni, hogy a kód a változtatás után is működik-e.
Az egységteszt segít kiküszöbölni az egyes modulokkal kapcsolatos kételyeket, és alulról felfelé építkező teszteléshez használható: először a program egyes részeinek, majd a program egészének tesztelésére.
Az egységteszteket a vizsgált osztály "élő dokumentumának" tekinthetjük . Azok az ügyfelek, akik nem tudják, hogyan kell használni ezt az osztályt, példaként használhatják az egységtesztet.
Mivel egyes osztályok más osztályokat is használhatnak, egyetlen osztály tesztelése gyakran kiterjed a kapcsolódó osztályokra is. Például egy osztály adatbázist használ; tesztírás közben a programozó rájön, hogy a tesztnek kölcsönhatásba kell lépnie az adatbázissal. Ez hiba, mert a teszt nem lépheti túl az osztályhatárt. Ennek eredményeként a fejlesztő elvonatkoztatja az adatbázis-kapcsolatot, és ezt a felületet saját álobjektuma segítségével valósítja meg . Ez kevésbé összefüggő kódot eredményez, minimálisra csökkentve a rendszer függőségeit.
A szoftvertesztelés kombinatorikus feladat. Például egy logikai változó minden lehetséges értékéhez két tesztre van szükség, egy IGAZ és egy FALSE értékre. Ennek eredményeként a forráskód minden sorához 3-5 sor tesztkódra lesz szükség.
Az olyan algoritmusok, mint a Marching cubes vagy a vörös-fekete fa elágazó döntési fával rendelkeznek, és hatalmas tesztcsomagokra van szükség az összes lehetőség ellenőrzéséhez: a GitHub egyik vörös-fekete fa implementációjában tizenkét tesztet végeztek a beillesztés ellenőrzésére [1] . A másikban automatikusan 10-et építenek! = 3,6 millió permutáció, és tapasztald meg mindet [2] .
Mint minden tesztelési technológia, az egységtesztelés sem teszi lehetővé az összes programhibát. Ez valóban abból következik, hogy a legegyszerűbb esetek kivételével a programvégrehajtás minden lehetséges útvonalát nyomon követni nem lehet.
Például a matematikai modellezésben . Az üzleti alkalmazások gyakran véges és megszámlálható halmazokkal, míg a tudományos alkalmazások folyamatosakkal dolgoznak . [3] Ezért nehéz az egyes programágakhoz teszteket kiválasztani, nehéz megmondani, hogy az eredmény helyes-e, a pontosság megmarad-e stb. És sok esetben a modellezés minőségét „szemmel” határozzák meg ”, és az utolsó eredmény „referenciaként” kerül rögzítésre. Ha eltérést talál, az új eredményt manuálisan ellenőrzi, és meghatározza, hogy melyik a jobb: a régi vagy az új.
A portokkal , időzítőkkel , felhasználóval és a rendszer egyéb "instabil" részeivel kölcsönhatásba lépő kódot rendkívül nehéz tesztelni elszigetelt környezetben.
Ez azonban nem jelenti azt, hogy az egységteszt itt teljesen alkalmatlan: arra kényszeríti a programozót, hogy például fájlokról és portokról absztrakt adatfolyamokra lépjen . Ez általánosabbá teszi a kódot (például probléma nélkül válthat fájlokról hálózati socketekre ), tesztelhetőbbé (a „kapcsolat megszakadt” helyzetet ellenőrizheti egy adatfolyam írásával, amely N bájt kiadása után balesetet szimulál; A Unix útvonalkonverziós funkciók Windows alatti ellenőrzése ) korlátozza azokat a részeket, amelyek nem esnek egységtesztelés alá.
Ez alapvetően a rendszer instabil része. Ezenkívül az egységtesztek általában egyszerűek, míg a többszálú rendszerek tesztjei éppen ellenkezőleg, meglehetősen nagyok.
Az egységtesztek végrehajtásakor mindegyik modult külön tesztelik. Ez azt jelenti, hogy az integrációs hibák, a rendszerszintű hibák, a több modulban végrehajtott függvények nem észlelhetők. Ezenkívül ez a technológia használhatatlan a teljesítményteszteknél. Így az egységteszt hatékonyabb, ha más tesztelési technikákkal kombinálják.
Az egységtesztelés előnyeinek kihasználása megköveteli a tesztelési technológia szigorú betartását a szoftverfejlesztési folyamat során. Nemcsak az összes elvégzett tesztről kell nyilvántartást vezetni, hanem a forráskód minden módosításáról is minden modulban. Erre a célra szoftververzió-vezérlő rendszert kell használni . Így, ha a szoftver egy későbbi verziója megbukik egy korábban sikeresen teljesített teszten, könnyen ellenőrizhető a forráskód változatai és kijavítható a hiba. Gondoskodnia kell arról is, hogy a sikertelen teszteket mindenkor nyomon kövesse és elemezze. Ennek a követelménynek a figyelmen kívül hagyása sikertelen teszteredmények lavinához vezet.
A legegyszerűbb esetek kivételével a tesztelt objektumnak kölcsönhatásba kell lépnie más objektumokkal. Ezek az "együttműködők" - csonkobjektumok - rendkívül egyszerűek: vagy rendkívül leegyszerűsítve (memória adatbázis helyett), vagy egy adott teszthez tervezték és mechanikusan megismétlik a cseremenetet. Problémák adódhatnak a csereprotokoll megváltoztatásakor, ilyenkor a csonk objektumoknak meg kell felelniük az új protokollkövetelményeknek. [négy]
Könnyen ellenőrizhető, hogy a modul működik-e a fejlesztő gépén. Nehezebb - mint a célgépen, gyakran nagyon korlátozott [5] .
Az extrém programozás az egyik posztulátumként feltételezi az automatikus egységvizsgáló eszközök használatát. Ezt az eszközkészletet harmadik fél (például Boost.Test) vagy az alkalmazás fejlesztőcsapata hozhatja létre.
Az extrém programozás egységteszteket használ a tesztvezérelt fejlesztéshez . Ehhez a fejlesztő a kód megírása előtt egy tesztet ír, amely tükrözi a modul követelményeit. Nyilvánvaló, hogy a kód írása előtti tesztnek nem szabad működnie. A további folyamat a legrövidebb kód írására redukálódik, amely megfelel ennek a tesztnek. Miután a fejlesztő sokszor megírja a következő tesztet, kódot és így tovább.
Az írási egységtesztek összetettsége a kód felépítésétől függ. Az egyes entitások (objektumorientált nyelvek osztályai) erős kohéziója vagy nagy felelősségi köre megnehezítheti a tesztelést. Csonkokat kell létrehozni a külvilággal kommunikáló objektumokhoz (hálózat, fájl I/O stb.). A terminológiában a „fejlettebb” csonkokat különböztetjük meg – a logikát hordozó hamis objektumokat . A tesztelés egyszerűbb is, ha a logikából a lehető legtöbbet tiszta függvényekre osztja fel . Semmilyen módon nem lépnek kölcsönhatásba a külvilággal, eredményük csak a bemeneti paraméterektől függ.
A tesztkódot külön könyvtárakba szokás szétválasztani. Kívánatos, hogy új tesztek hozzáadása a projekthez ne legyen nehéz feladat, és minden tesztet le lehessen futtatni. Egyes verziókezelő rendszerek, például a git, support hook-okat ( angol hook ), amelyekkel a változtatások végrehajtása előtt beállíthatja az összes teszt elindítását. Ha legalább az egyik teszt sikertelen, a változtatások nem kerülnek végrehajtásra. Folyamatos integrációs rendszerek is alkalmazhatók .
Vannak egységtesztelő eszközök és könyvtárak a legnépszerűbb magas szintű programozási nyelvekhez. Néhány közülük:
Egyes nyelvek szintaktikai szinten támogatják az egységtesztet. Ezzel szükségtelenné válik annak kiválasztása, hogy melyik keretrendszerhez kíván kapcsolódni, és egyszerűbbé válik a kód más projektekbe történő portolása.
Példa ilyen nyelvekre:
Kódpélda D nyelven
osztály ABC { this () { val = 2 ; } magán int val ; public func () { val *= 2 ; } } unittest { ABC a ; a . func (); assert ( a . val > 0 && a . val < 555 ); // a modulon belül hozzáférhet egy privát változóhoz }