Integrációs és ellenőrzési technikák (VIMIA04) Unit tesztelés Honfi Dávid, Micskei Zoltán Méréstechnika és Információs Rendszerek Tanszék Budapesti Műszaki és Gazdaságtudományi Egyetem Méréstechnika és Információs Rendszerek Tanszék 1
Áttekintés Funkció Felülvizsgáló Verziókezelő rendszer Fejlesztő Éles környezet Kódolási szabályok Statikus analízis Egységteszt Folytonos integráció kiszolgáló Felügyelet Rendszerteszt 2 E2E teszt Ikonok: icons8.com
Helye a fejlesztési folyamatban V-modell Design Agilis modellek Requirements Implementation, Developer test Evaluation Acceptance test 3
Mi a unit teszt? Tesztelt rendszer (SUT) egy részét vizsgálja Jól körülhatárolt rész: unit, unit under test (UUT) o Osztály o Metódus Unit teszt 4
Miért jók a unit tesztek? Könnyen elkészíthetők Laza csatolásra kényszerít: tesztelhetőség Azonnali visszajelzés Korai hibamegtalálási lehetőség Forrás: Applied Software Measurement, Capers Jones, 1996 5
Hogy néz ki egy unit teszt? (JUnit) public class ListTest{ Tesztosztály List list; // SUT 1. Beállítás @Before public void setup(){ } list = new List(); Inicializáció Teszteset } @Test public void add_emptylist_success(){ list.add(1); 2. Végrehajtás assertequals(1, list.getsize()); } 4. Lebontás? 3. Ellenőrzés 6
Hogyan lehet jó a unit teszt? 1. Alapelvek betartása 2. Bevált minták követése 3. Smell -ek elkerülése Gerard Meszaros. 2006. xunit Test Patterns: Refactoring Test Code. Prentice Hall PTR, Upper Saddle River, NJ, USA. http://xunit.org 7
xunit Test Patterns könyv ajánlásai Forrás: Gerard Meszaros, http://xunitpatterns.com/ 8
ALAPELVEK 9
Tesztelhetőségre tervezés Tesztvezérelt tervezés (Test-Driven Development) o Tesztek készülnek el elsőként (TDD) o A kész tesztek kikényszerítik a tesztelhető kódot Vezérlési pontok (Control point) o UUT vezérlése kívülről (API vagy back door) o UUT adott állapotba állítása Megfigyelési pontok (Observation point) o UUT állapotának lekérése o UUT kommunikációjának megfigyelése 10
Helyettesítők Függőségek Helyettesítő objektumok o Eredeti helyett használjuk: megfigyelés, beavatkozás o Fontos: tesztelhetőségre tervezés 11
Tesztek szervezése Egy UUT-hez egy tesztosztály Tesztosztály Teszteset Teszteset UUT Egy tesztosztály egy funkcióhoz Tesztosztály Teszteset Teszteset Feature UUT Tesztesetek neve tartalmazza a következőket o UUT neve testapprove_shouldendupinscheduledstate() o A tesztelt metódus/feature neve o Adott bemenetek legfontosabb tulajdonságai o Egyéb állapotinformáció UUT-vel kapcsolatban 12
Automatizált futtatás követelményei Tesztelhetőségre tervezés Front door priorizálása Ne módosítsuk a UUT-t (helyette: leszármazott) Függetlenítsük a teszteket Izoláljuk a modult 1 ellenőrzött követelmény tesztenként 13
UNIT TESZTELÉSI MINTÁK Alapelemek 14
I. Végrehajtás Végrehajtó (pl. xunit, JUnit, MSTest) o Tesztek felsorolása, felfedezése, kiválasztása o Tesztek nyomon követése: darabszám, eredmények Tesztkészlet: csoportosításhoz o Tesztesetek összefogása tesztosztályba o Több tesztosztály összefogása Felfedezés: osztály vagy metódus szinten is o Elnevezés alapján o Attribútum segítségével o Könyvtárstruktúrával 16
Metódus o Egyszerű teszteset II. Tesztdefiníció Happy path : egyszerű lefutási útvonal, nagy kódfedés Eredményeket ellenőrzi, nem vár kivételt Arrange Act Assert törzs o Elvárt kivételes teszteset A teszt futtatása során kivételt várunk Ha nem keletkezik, a teszt hibát jelez o Konstruktor teszteset Ha egy konstruktor összetett logikát tartalmaz Egyszerű teszteset, de elvárhat kivételt is (rossz argumentum) Ellenőrzés: objektum állapota megfelelő-e 17
Osztály II. Tesztdefiníció o Nagyobbrészt tesztmetódusokból épül fel o Utility metódus: duplikáció elkerüléséhez o Helper: osztály több tesztosztályhoz tartozó utility metódusok számára Tesztosztály Teszteset Teszteset Helper Utility Utility Tesztosztály Teszteset Utility 18
Friss III. Inicializálás o Minden teszteset friss környezetben fut o A környezet futás előtt közvetlenül kerül létrehozásra o Minden teszteset teljesen független egymástól Megosztott o Több eset használhat egy előre definiált környezetet o Futás idő csökkenthető o DE: tesztesetek hatással lehetnek egymásra o Lehetőleg kerülendő megoldás o Használata csak ellenőrzött körülmények között 19
IV. Eredmények ellenőrzése Állapot ellenőrzése o Procedurális: assertionök sorozatával o Objektum: adott állapotú objektummal Viselkedés ellenőrzése o Procedurális: a teszt futása közben elkapva o Elvárt: teszt futása előtt definiált helyettesítővel 20
V. Befejezés Stratégiák o GC Referenciák megszüntetése Eszkalálás o Automatikus Registry használata: pl. kollekció Végén törlés egyesével Szervezés o Inline: minden teszteset végén o Implicit: futtató rendszer hív egy külön metódust 21
Példa JUnit 4 Tesztkészlet inicializálás és befejezés Teszteset inicializálás és befejezés Tesztesetek definíciója 22
VI. Helyettesítők (visszatekintés) Függőségek Helyettesítő Data access Helyettesítő Import/Export Teszt hívás (GUI) A helyettesítők indirekt bemenetet adnak. 23
Stub VI. Helyettesítők típusai o Előzetesen megadott értékeket ad vissza o Nem tartalmaz ellenőrzést o Responder: valid értékek beadása, happy path o Saboteur: invalid értékek beadása o Átmeneti: üres osztály, beégetett értékekkel Spy o Indirekt kimenetek/hívások megfigyelésére(!) o Tesztesetek végén spy ellenőrzése assertekkel o Pl. anoním függvény átadása SUT-nak, registry 24
Mock objektum VI. Helyettesítők típusai o Előzetesen definiált értékek visszaadása ÉS ellenőrzés o Előzetes elvárások Hívási argumentumok Hívások darabszáma o Hatással lehet a teszteset kimenetelére (assert) Fake objektum o Light-weight implementáció (csak a lényeg marad) o Interakciót képes kezelni (hívási szekvenciák) o Példák: fake adatbázis, fake web service, fake réteg 25
Stub példa Stub Teszt 26
Mock példa 27
UNIT TESZTELÉSI MINTÁK Tesztelhetőségre tervezés 28
Dependency injection UUT Foo(int):bool C osztály A hívó félnek kelljen a függőségek átadását biztosítani! Lehetőségek o Konstruktorban o Paraméterként híváskor o Kettő között: setter Másik ötlet: dependency lookup Hogyan adjuk át a stubot a C-hez a tesztben? 29
Humble objektum A lényeges logikát szeparáljuk a környezettől A szeparált rész könnyen tesztelhetővé válik Lehetőségek o Extract method ( Poor man s ) o Ősosztályok és leszármazottak o Külön osztály a kiszervezett logikával (delegálás) 30
Teszt hook Tesztelés módosítja a tesztelt viselkedést o Lehetőleg kerülendő végső megoldás o Csak szigorú feltételek mellett használható A kód elágazásokat tartalmaz: if(testing) test else prod A tesztelhetetlen részek tesztelhetővé válnak Pl. dátum lekérdezése DateTime stub o Nem mindig lehet injektálni a stub-ot o Teszteléskor DateTime.Now helyett adott inicializálás 31
TESZT SMELLEK 32
I. Homályos (obscure) tesztek Nehéz elsőre megérteni a tesztet. Hatások o Magasabb fenntartási költségek o Több bug maradhat benn a rosszul írt kód miatt Okok o Nincs kellő figyelem a tesztek egyszerűségén o In-line megoldások tesztekben: összetett logikák sora 33
I. Homályos tesztek tipikus esetei Kíváncsi (eager) teszt: túl sokat akar ellenőrizni Mystery Guest: o Nem látszik az akció-hatás összefüggés a tesztben o Pl.: a teszteset metódusán kívül is történik lépés Túláltalánosítás: felesleges környezet felépítése Beégetett tesztadatok Indirekció: a teszt az UUT-t indirekten éri el 34
Példa Homályos teszt 35
II. Feltételes tesztlogika A tesztnek van olyan része, ami nem fut le minden futtatáskor. Hatások o Elbonyolítja a tesztet nehezíti a megértést o Nehezebb karbantartani Okok o Visszatéréstől függő tesztlogika o Kollekciók, összetett objektumok ellenőrzése o Újrafelhasználhatóságra törekvés 36
III. Duplikáció Ugyanaz a tesztkód többször ismétlődik. Hatások o Karbantarthatóság erős romlása Okok o Idő: CTRL+C és CTRL+V sokat gyorsít a tesztek írásán o Képesség: tesztfejlesztő refaktorálási képességei 37
IV. Assertion rulett Nehéz megállapítani, hogy a sok assertion közül melyik okozza a teszt hibáját. Hatások o Problémák azonosítása nehézzé, költségessé válik o Megnöveli a hibajavítások idejét Okok o Tesztesetek számának minimalizálása több ellenőrzés o Hiányzó assertion üzenet 38
Példa Assertion rulett 39
V. Instabil teszt Ugyanaz a teszteset néha hibamentes, néha hibát jelez. Okok o Nemdeterminizmus a tesztelt kódban o Erőforrásszivárgás o Erőforrásoptimalizálás o Interakciót végző teszt (pl. környezet) 40
VI. Törékeny teszt Adott teszteset nem fordul, vagy hibásan fut egy olyan változtatás után, amit a teszt nem érint. Okok o Indirekt tesztelés o Kíváncsi teszt o Interfész-érzékenység o Viselkedés-érzékenység o Adatérzékenység o Kontextus-érzékenység 41
VII. Lassúság Tesztfuttatás túl sok időt vesz igénybe. Okok o Lassú a tesztelt komponens (pl. nem izolált) o Általános környezet: túl sok inicializálás az elején o Aszinkronitás o Túl sok teszt 42
Összefoglalás Unit teszt: fontos a kódminőség/költségek miatt Unit tesztek minősége nem triviális Alapelvek o Tesztelhetőség o Szervezés o Automatizálás Bevált minták: tervezés, definíciók, helyettesítők Smellek elkerülése 43