Kivételkezelés Tesztelés, hibakeresés, kivételkezelés előadás http://nik.uni-obuda.hu/prog2 Szénási Sándor szenasi.sandor@nik.uni-obuda.hu Óbudai Egyetem,Neumann János Informatikai Kar
Tesztelés Hibakeresés Kivételkezelés Saját kivételek készítése Kivételkezelés
3 Tesztelés Tesztelés: próbafeladatok lefuttatásával a program hibáinak felfedezése a cél Teszteset: a be- és kimeneti adatok együttes megadása A jó teszteset követelményei Nagy valószínűséggel egy még felfedetlen hibára derít fényt Későbbiekben megismételhető, különben a hibakeresést ellehetetleníti Teszteseteket mind az érvénytelen, mind az érvényes adatokra célszerű választani Minden tesztesetből a lehető legtöbb információt kell kinyerni, ezzel kell csökkenteni a tesztesetek számát Tesztelési módszerek két csoportja: Statikus tesztelés a programot nem futtatjuk Dinamikus tesztelés a programot futtatjuk
4 Statikus tesztelési módszerek Kódellenőrzés Az implementált programkód vizsgálata Gyakran egy másik fejlesztő tudja ezt hatékonyan megtenni Formai ellenőrzés Szintaktikai hibák kiszűrése Nem létező kulcsszavak, változók, függvények stb. Keresése Fordítóprogramok általában elvégzik (hibák) Tartalmi ellenőrzés Nem használt változó Nem használt változóérték Hiányzó kezdőérték Konstans kifejezések Visszatérési érték nélküli függvény Végtelen ciklusok Fordítóprogramok ezek jelentős részét szintén jelzik, bár önmagukban nem minősülnek hibának (figyelmeztetések)
5 Dinamikus tesztelés fekete doboz módszer Fekete doboz módszer = adatvezérelt tesztelés Nem veszi figyelembe a program belső szerkezetét Csak a bemenő és kimenő adatok kapcsolatára épít Ekvivalencia osztályok keresése A bemeneti tartományt részekre kell osztani, és ezeken belül jellemző teszteseteket kell választani Tartomány esetén: alatta közte fölötte Logikai érték esetén: kielégítő nem kielégítő feltétel Érvényes és érvénytelen esetekre is fel kell állítani az ekvivalencia osztályokat Egy teszteset lehetőség szerint minél több bemeneti feltételt elégítsen ki, így kevesebb esetre van szükség Határeset elemzés Az ekvivalencia osztályok kiválasztott elemeinek a határértékeket választja Nemcsak a bemenetre, hanem a kimenetre is célszerű ekvivalencia osztályokat felállítani
6 Dinamikus tesztelés fehér doboz módszer Tesztelést a program ismeretében végezzük el Ismertek a használt algoritmusok Ismert a pontos implementáció (forráskód) A módszer három lépését különböző technikák segítik Kipróbálási stratégiák Meghatározza, hogy a programgráf melyik útjait kell tesztelni Utasítások egyszeri lefedésének elve úgy válasszuk meg a teszteket, hogy minden utasítást elérjen a vezérlés Döntés lefedés elve elágazások esetén minden ágat elérjen Részfeltétel lefedés elve összetett feltétellel rendelkező elágazások esetén minden részfeltétel kipróbálásra kerüljön Tesztesetek generálása Az előzőleg meghatározott utakon végigvezető bemenetek meghatározása Pl. szimbolikus végrehajtás (részletesen nem tárgyaljuk) Ekvivalencia osztályok megállapítása Előzőleg megismert módszerrel tesztelés folytatása
7 Speciális tesztek Funkcióteszt Az elkészült program tartalmaz-e minden szükséges funkciót? Biztonsági teszt A program ellenőrzi a bemeneti adatokat? Ellenőrzi-e a perifériákhoz, állományokhoz való hozzáférést? Stressz teszt Fel tud-e dolgozni nagy sebességgel érkező adatokat? Alkalmas-e párhuzamosan, több irányból érkező nagymennyiségű adat feldolgozására? Volumen teszt Alkalmas-e nagy mennyiségű adat feldolgozására? Ha igen, romlik-e a program hatékonysága? Modulteszt, összeépítési teszt A program önálló moduljainak egymástól független ellenőrzése Az önállóan már letesztelt modulok összeépítése utáni teszt
Tesztelés Hibakeresés Kivételkezelés Saját kivételek készítése Kivételkezelés
9 Hibakeresés és javítás Hibakeresés: a tesztelés során feltárt hibák helyének megállapítása A fejlesztői környezetek általában fejlett eszközöket nyújtanak a hibakereséshez (nyomkövetés stb.) A hibák száma általában a program méreténél gyorsabban növekszik (a fejlesztés természetes velejárójának tekinthető) Néhány tipikus hiba Szintaktikai hiba Végrehajtás közben jelentkező hiba Program nem áll meg Program megáll, de nem ad eredményt Program megáll, de hibás eredményt ad Hibajavítás: a hibakeresés során megtalált hiba javítása A hibát kell kijavítani, nem csak a tüneteit megszüntetni Hibajavítást újabb tesztelésnek kell követnie Hibajavítás visszanyúlhat a fejlesztés előző fázisaiba
10 Indukciós, dedukciós módszer Indukció: a minta azonos nemű elemeinek egy közös tulajdonságából következtethetünk arra, hogy minden ilyen nemű elem rendelkezik ezzel a tulajdonsággal A rendelkezésre álló teszteseteket rendszerezni kell aszerint, hogy melyik okozott hibát és melyek nem Fel kell állítani egy feltételezést, hogy milyen tulajdonságú elemek tartoznak az egyik ill. másik csoportba (amennyiben szükséges, további tesztekkel ez megerősíthető) Ha már egyértelmű(nek tűnik) a hibát okozó tulajdonság, a programon átvezető tesztutak közül ki kell válogatni azokat, amelyek csak az ilyen esetekben futnak le Ezeken a pontokon célszerű keresni a hiba okát Dedukció: ha minden azonos neműről tudjuk, hogy rendelkezik egy tulajdonsággal, akkor ez az egyedre is igaz Össze kell gyűjteni az összes feltételezhető hibaokot A minta elemei alapján ki kell zárni azokat, amelyek nem valósak Az így megtalált okok után a folytatás hasonló az előzőhöz
11 Visszalépés, teszteléssel összevonás Visszalépéses technika A hiba egyik előfordulási helyéből kell kiindulni (ilyen biztosan ismert, legrosszabb esetben a végeredmény megjelenítése) Ebből a pontból a programot visszafelé végrehajtva kell megkeresni a rendellenességet okozó pontot Amennyiben a visszafelé görgetett hibás részeredmény helyett egy helyes részeredmény jelenik meg, valószínűleg az (első) hibás sor volt az utolsó lépés Gyakran fejlesztői eszközök segítik ezt a módszert Teszteléssel segített hibakeresés Úgy kell megválasztani az egymást követő teszteseteket, hogy a teljes programgráfban bejárt út csak egy-egy elemi (vagy legalábbis minél kisebb) változással járjon Amennyiben egy ilyen elemi lépés távolságban lévő teszteset pár egyik tagja hibás, a másik helyes működést eredményez, szintén lehet következtetni a hiba helyére
12 Egyéb lehetőségek Fejlesztőeszközök által nyújtott funkciók Szintaktikai ellenőrzés Program lépésenkénti futtatása Változók értékeinek nyomon követése Töréspontok elhelyezése a programkódban Adatok értékeire vonatkozó töréspontok Hívási verem megtekintése Változók értékeinek menet közbeni megváltoztatása Vezérlés aktuális pontjának megváltoztatása Programkód futás közbeni módosítása Egyéb megoldások Programozási nyelvek által nyújtott hibakezelési lehetőségek Tipikusan pl. a napjainkban elterjedt kivételkezelés Egyéb megoldások
Tesztelés Hibakeresés Kivételkezelés Saját kivételek készítése Kivételkezelés
14 Hagyományos módszerek Feladat: számoljuk ki óránként az átlagsebességet Hibakezelés hagyományos módon: függvény Átlagsebességek(SEB, N) i 1; vanértelme igaz ciklus amíg i 24 vanértelme sum 0 ciklus j 1-től N i -ig sum sum + SEB i,j ciklus vége ha N i 0 akkor átlagok i sum / N i különben vanértelme hamis elágazás vége ciklus vége ha vanértelme akkor Átlagsebességek átlagok különben Átlagsebességek Ø elágazás vége függvény vége
15 Hagyományos módszer hátrányai Nehezen olvasható kód Meglehetősen hosszú kódot eredményez Nehezen áttekinthető, mivel keveredik a tényleges kód az ellenőrző műveletekkel (és az esetleges javításokkal) Egy hibaforrást gyakran csak több művelettel lehet kiszűrni Nehezen programozható Minden műveletet meg kell vizsgálni, hogy milyen bemenetekre adhat hibás választ, és ezeket mind ki kell zárni Ez meglehetősen nehéz feladat, hiszen fordított gondolkodást igényel a műveletek esetén Nehezen karbantartható Gyakoriak a mélyen egymásba ágyazott feltételek Egymásbaágyazott szerkezetek esetén egy mélyebb szinten keletkezett hiba esetén biztosítani kell a kilépést A feltételeknek általában nincs különben águk Ha ugyanaz a hibalehetőség a kód több helyén is fennáll, akkor gyakran duplikálni kell az ellenőrzést
16 Hagyományos módszer hátrányai Függvényhívás esetén a visszatérési értékének valamilyen kitüntetett értéke jelzi, hogy hiba történt Az érték egyben a hiba jellegére is választ ad Egy másik függvény meghívásával lehet lekérdezni a pontos hibakódot Használatának hátrányai Különböző típusú visszatérési értékeknél más-más megoldást kell alkalmazni (van ahol nem járható út, pl. összeadás) Könnyű átsiklani egy ilyen visszaadott hibakód felett A módszer használatának okai Gyakran hatékonyabb, mint a kivételkezelés Hagyományos programozási nyelvekben nem volt más megoldás Rögzült programozói szokások
17 Kivételkezelés Kivételkezelő blokk egy lehetséges megvalósítása try <védett blokk> catch (típus eseményref) <típus1 kivétel kezelése> catch (típus2 eseményref) <típus2 kivétel kezelése> finally <mindig lefut> end Általában három különböző blokkot különböztetünk meg try védett (próbára tett) szakasz Ide helyezzük azokat az utasításokat, amelyek esetleg hibákat (vagy egyéb kivételt) okozhatnak catch hibakezelő szakasz Erre a blokkra kerül a végrehajtás, ha a megadott típusú kivétel keletkezett a védett szakaszon belül finally lezáró szakasz Tartalmaz minden kritikus utasítást, aminek mindig le kell futni
18 Jól áttekinthető kód Programkód könnyebben írható Nincs szükség műveletenkénti elemzésre, hogy ki tudjuk zárni az esetleges hibás bemenetből adódó hibákat Fizikailag maga a programkód is rövidebb, egyszerűbben implementálható Programkód könnyebben olvasható Élesen elválik egymástól a programkód általános (lényegi) része a kivételes esetek kezelésétől Így nem csak a kivételek kezelése, hanem a lényegi kód is áttekinthetőbbé válik Programkód könnyebben karbantartható A kivételkezelő blokkok egymástól függetlenek, így az egyik változtatása nem befolyásolja a többit Egy esetleges változtatás során nem jellemző, hogy egy továbbgyűrűző, a többi blokkot is módosító változást okozna Szabványos megoldásnak tekinthető, így más fejlesztők számára is könnyen értelmezhető
19 Kivételes esetek központi kezelése Egy blokk minden kivételét egy központi helyen kezeli Akár egyetlen programsor futtatása is okozhat több különböző hibát (akár egyszerre többet is) A kivételkezelő blokkal nem kell minden lehetséges hibát egyesével felsorolni, egy blokk (megfelelően paraméterezve) alkalmas a védendő kód minden kivételét egyszerűen kezelni Kivétel típusok szerinti kivételkezelő kiválasztás Hagyományos kivételkezelési technikákkal csak egy kivételkezelő blokk megadására volt lehetőség, jelenlegi technikák mellett kivételtípusonként pontosíthatunk A védett részben több helyen is keletkezhetnek azonos típusú kivételek, lehetőség van ezeket egy helyen kezelni A kivételek általában hierarchiát alkotnak, így rugalmasan lehet definiálni a kivételkezelő blokk típusát A gyakorlatban ez lényeges, mivel általában az azonos típusú hibákat azonos módon lehet kezelni (gyakran sehogy)
20 További előnyök Nem hagyható figyelmen kívül Megfelelő alkalmazás esetén nincs szükség a függvények visszatérési értékének ellenőrzésére Amennyiben egy kivétel keletkezik, akkor a futtató környezet mindenképpen megkeresi a hozzá legközelebbi kezelő blokkot Amennyiben nincs ilyen blokk, akkor a program futása megszakad Ez teljesen kizárja a program hibás adatokkal való továbbfutását Hibakeresés elősegítése A kivételek általában a hiba típusán túl további adatokat is tárolhatnak Így egy kivétel információkkal szolgálhat arról, hogy mikor és miért váltódott ki, akár egészen részletes adatokkal is (milyen művelet, milyen paraméterekkel stb.) Ez jelentősen csökkenti a nyomkövetéssel töltött időt
21 Egymásba ágyazott kivételkezelők Strukturált programoknál gyakoriak az egymásba ágyazott szerkezetek (elágazás, ciklus, szubrutinhívás), a kivételek kezelésénél erre is figyelmet kell fordítani Strukturált kivételkezelés esetén a bekövetkező kivételek kezelése nem egy megadott ponton történik, hanem az egymásba ágyazott kivételkezelő blokkok hierarchiájától függő helyen Ennek egy lehetséges megvalósítása A program futása során veremben tárolja az elért kivételkezelő blokkokat, és azok paramétereit: minden blokkba való belépéskor elhelyez egy keretet a veremben, a blokkból való kilépéskor pedig ezt kiveszi Kivétel létrejötte esetén a futtató környezet a verem alapján keresi meg a megfelelő kivételkezelő blokkot Többszálú környezetben ezek általában szálanként függetlenek A nem kezelt kivételeket végül a futtatókörnyezet kapja el Kivétel keletkezésének és a kezelésének helye elválik!
22 Try, catch blokkok A try blokk használata Tartalmazza azokat a programsorokat, amelyeknél felmerül valamilyen kivétel lehetősége Kritikus programrészeket célszerű így használni, nélküle egy kisebb (esetleg javítható) hiba is a program leállításához vezet A catch blokkok használata Általában több catch blokk használatára van lehetőség, különböző kivételtípusok meghatározásával Egy ilyen blokk kezeli a try blokkon belül történt összes megadott típusú kivételt, ha ez nem megfelelő, akkor a try blokkot több részre kell bontani Ha több blokk is alkalmas egy kivétel kezelésére, általában csak a felülről első megfelelő blokk fut le A catch blokkban lehetőség van elérni magát a kivétel objektumot is, ami további információkat hordozhat Ha nem történt kivétel, nem kerül rá a vezérlés
23 Finally blokk, kivételek továbbdobása A finally blokk használata Itt célszerű megadni azokat a műveleteket, amelyeket mindenképpen le kell futtatni (pl. erőforrások felszabadítása) Függetlenül attól, hogy létrejött-e kivétel, vagy sem, ez a blokk mindig lefut (ha volt kivétel, akkor a kivételkezelő után) Amennyiben történt kivétel, de nincs hozzá tartozó kivételkezelő, ez a blokk akkor is lefut Figyelmet igényel azonban, hogy a finally blokkban csak olyan erőforrásokat szabadítson fel a program, amik biztosan le lettek foglalva (ez a try részen belül nem mindig garantálható) Előfordulhat, hogy egy kivételkezelő (vagy finally) blokkban újabb kivétel keletkezik Egy újabb nem várt esemény miatt A már kezelt kivétel továbbítása miatt (újradobás) Egy más típusú kivétel eldobása miatt
Tesztelés Hibakeresés Kivételkezelés Saját kivételek készítése Kivételkezelés
25 Hagyományos módszer hátrányai Az objektumorientált nyelvek a kivételeket általában egy objektumba csomagolva kezelik (ill. magát a kivételt egy objektumnak tekintjük). Ennek előnyei: Öröklődés Saját kivétel készítésekor lényeges lehet Polimorfizmus Egyszerre több kivételt kezelő blokk készítése esetén További információ hordozása A kivételobjektum típusa már önmagában is jellemzi a bekövetkezett kivételt, de emellett számos egyéb adatot tárolhat ez az objektum (kivétel létrejöttének oka, pontos helye, paraméterek, egyéb üzenet stb.) Ennek megfelelően a kivételek általában egy hierarchiát alkotnak, amelynek felépítése programozási nyelvenként egészen különböző is lehet
26 Kivételek típusai Kivétel készítője alapján Keretrendszer saját kivételei Egyéb fejlesztők által készített kivételek Kivétel forrása alapján Szoftver kivételek pl. általános kivételek, jelzések Hardver/operációs rendszer szintű kivételek pl. erőforrás problémák, kritikus hibák Bizonyos nyelvekben kötelező ellenőrzés alapján Kötelezően ellenőrzött kivételek Ezeket a kivételeket kötelező ellenőrizni (vagy továbbadási szándékunkat jelölni), különben a fordító fordítási hibát jelez (pl. hálózati kapcsolat megnyitása esetén) Nem kötelezően ellenőrzött kivételek Ellenőrzésük nem kötelező, a programozóra van bízva ennek eldöntése (pl. aritmetikai hibák ellenőrzése)
27 Kivétel osztály definíciója Saját kivételosztály definiálása általában nem különbözik egy általános osztály definíciótól A saját kivételosztály őse célszerűen az adott környezet (programozási nyelv) megfelelő beépített kivételoszálya Egy kivétel általában az alábbi információkat tartalmazza: Kivétel neve célszerű már a kivétel nevét is beszédesen megválasztani, hogy az utaljon a kiváltás okára Üzenet szöveges mezőben tetszőleges üzenet továbbítása a kivétel kezelője felé Hívási verem a hívási verem állapota a kivétel létrejöttekor Beágyazott kivétel kivétel újradobása esetén tartalmazhatja az előző (már kezelt) kivételt A kivétel a többi osztályhoz hasonlóan tartalmazhat más nyelvi elemeket is (metódus, statikus tagok stb.)
28 Saját kivétel használata Kivétel dobása Az osztálykönyvtár kódjának kiegészítése, az esetleges hibák bekövetkezte esetén az előzőleg definiált kivételosztály példányosítása, majd a kivétel dobása Természetesen nem csak saját magunk által definiált kivétel objektumokat lehet így dobni A programkód minden olyan helyén célszerű ezt a technikát alkalmazni, ahol hibát (nem végrehajtható műveletet) fedezünk fel, de az adott szinten ezt nem tudjuk kezelni Az előzőleg meghatározott adatokat (pl. üzenet, egyéb saját mezők) is célszerű a kivételobjektumban beállítani Kivétel elkapása A saját kivételek elkapása és kezelése általában nem különbözik a keretrendszer kivételeinek kezelésétől A hibakeresés elősegítése végett célszerű minél több információt elhelyezni a kivételben annak okáról
29 Megjegyzések/javaslatok Hibakezelés felelőssége Az osztálykönyvtárak készítői előre nem tudhatják, hogy mikor, milyen okból használják majd az ő kódjukat, emiatt célszerű azokat a lehető legrobusztusabban elkészíteni Az alkalmazásfejlesztők viszont az osztálykönyvtár működését nem fogják ismerni, emiatt az osztálykönyvtár készítőjének felelőssége, hogy érthető módon jelezze a hibákat Célszerű kihasználni a finally blokk előnyeit A catch blokkok írása nélkül is célszerű az erőforrások felszabadítását ilyen blokkba helyezni Csak a valóban kezelt kivételeket érdemes elkapni Gyakran rossz programozói gyakorlat az összes kivétel elkapása, mivel így azok nem jutnak el a megfelelő kezelőig Részlegesen teljesített feladatok visszagörgetése A kivétel kezelése nélkül is gyakran célszerű egy minden kivételt elkapó catch ág, ami visszagörgeti az eddigi változtatásokat (és továbbdobja a kivételt)
30 Irodalomjegyzék Javasolt/felhasznált irodalom Pap, Szlávi, Zsakó: μlógia21 Módszeres programozás: A programkészítés technológiája ELTE TTK, 2000 J. Richter: CLR via C# 2nd edition Microsoft press, 2006 R. Simmons: Hardcore Java O Reilly, 2004