Egység és integrációs tesztelés¶
Az egységtesztelés egy olyan módszer, amely a kód egyes egységeinek elkülönítésére és tesztelésére szolgál az egyes komponensek hatékonyságának meghatározása érdekében. Ez a módszer a szoftver tesztelése helyett kisebb részekre bontja azt, hogy biztosítsa az egyes komponensek helyességét. Az egységtesztek jellemzően a fejlesztési fázisban zajlanak, lehetővé téve a csapatok számára, hogy a szoftver kiadása előtt azonosítsák és kijavítsák a problémákat. Az egységtesztek figyelmeztetik a fejlesztőket a lehetséges hibákra vagy hiányosságokra, amelyek a jövőben problémákat okozhatnak, és javítják az általános minőséget és teljesítményt.
Az integrációs tesztelés a szoftvertesztelés egyik alapvető szempontja, amelynek célja annak felmérése, hogy a különböző alkalmazások mennyire hatékonyan integrálódnak egymással. Amikor különböző szoftvermodulokat egyesítünk, az integrációs tesztelés vizsgálja, hogy azok hogyan működnek együtt, és felderíti az esetleges hibákat. Ez különösen fontos, mivel a zökkenőmentes integráció biztosítja a szoftvermodulok hatékonyságát. Az integrációs tesztelés általában az egységtesztelés után történik, és az egyesített szoftverelemek általános illeszkedését és működését értékeli. A tesztek automatizálása segít korán felismerni a problémákat, mielőtt azok összetettebbekké válnának.
JUnit¶
A JUnit 5 egységteszt keretrendszerrel fogunk megismerkedni. JUnit 5 a Java és a JVM számára készült programozóbarát tesztelési keretrendszer ötödik fő verziója. Ez a modern és rugalmas keretrendszer támogatja a Java 8-at és újabb verziókat, valamint különböző tesztelési stílusokat.
Jellemzői¶
- Az xUnit család tagja, Java implementáció.
- Reflection-t használ a tesztek futtatásához.
- Könnyen készíthetők új tesztek.
- Tesztcsoportok (suite) készíthetők.
- Többféleképpen futtatható, azonnali eredményt ad.
- Automatizálható és a keretrendszerekbe könnyen beépíthető.
- Nem minden esetben használható!
- GUI tesztelés csak korlátozott módon (mock) lehetséges.
Mi az a mock?
A mock a szoftvermérnöki területen egy szimulált objektum vagy modul, amely helyettesíti a valódi objektumot vagy modult. A mockokat gyakran tesztelés során használják, hogy elkülönítsék egy adott modul vagy komponens viselkedését, és ellenőrizzék, hogy az várt módon működik. A mock tesztelés során a függőségeket olyan objektumokkal helyettesítik, amelyek szimulálják a valódiak viselkedését. Ennek célja, hogy a tesztelt kódra összpontosítsunk, és ne az externális függőségek viselkedésére vagy állapotára.
GUI tesztelése
A GUI tesztelésére nincs dedikált óránk, a kiadott projektfeladatokon keresztül találkozhatnak vele a hallgatók. A GUI-t gyakran manuálisan tesztelik, de léteznek erre a célra is jól használatható eszközök. Az automatizálás gyakran felvétel-visszajátszás párokra épül, amelynek segítségével az ismétlődő lépéseket tudjuk automatizálni.
GUI tesztautomatizálás eszközei:
- Selenium: a projekt honalapja itt található
- CubicTest: GitHub-ról letölthető nyílt forráskódú eszköz
- SWTBot: Eclipse projekt, ezen az oldalon tölthető le
További automatizálási eszközök és azok leírásai találhatók ezen a linken.
Junit 5 assert metódusai¶
Tesztelés közben az assert metódusokkal ellenőrizzük, hogy a megfelelő eredményeket kapjuk-e az érintett metódus végrehajtása során. Ha nem megfelelő a kapott eredmény, a teszteset sikertelennek minősül (failed). Statikus metódusokat használunk (Java 5.0 – static import).
- Az
assertFalse(BooleanSupplier condition)
,assertTrue(BooleanSupplier condition)
metódusokat logikai értékek tesztelésére használuk. - Az
assertEquals(expected, actual)
metódus az elvárt és kapott eredmények összehasonlítását végzi. Az expected és actual paraméterek minden Java-ban támogatott típust felvehetnek, a metódusnak minden típusra létezik overloadolt változata. - Az
assertIterableEquals(Iterable<?> expected Iterable<?> actual)
metódus paramétereiIterable
adatstruktúrák. A metódus a struktúrák minden egyes összetartozó elemén elvégzi az összehasonlítást. Az összetartozó elemek az ugyanazon pozíción lévő elemeket lesznek azexpected
ésactual
paraméterek esetében. - Az
assertNotEquals(Object unexpected, Object actual)
az elvárt és ténylegesen kapott eredmények különbözőségét teszteli. - Az
assertArrayEquals(expected[], actual[])
metódus paramétere két tömb. A metódus a tömb elemeinek összehasonlítását végzi. - Az
assertNull(Object actual), assertNotNull(Object actual)
metódusok szintén gyakran használtak, a paraméterül kapott objektum NULL értékét, illetőleg annak tagadását vizsgálják. - Az
assertSame(Object expected, Object actual), assertNotSame(Object unexpected, Object actual)
metódusok objektumok azonosságát, illetőleg különbözőségét nézik. Ebben az esetben nem csupán az objektumok által felvett értékek azonosságának vizsgálata a cél, hanem az is, hogy a két objektum ugyanaz az objektum-e, vagyis ugyanazon címen lelhető-e fel a memóriában. - Az
assertThrows(Class expectedType, Executable executable)
metódus akkor assertál, ha az executable az elvárt típusnak megfelelő kivételt dobja. - Az
assertTimeout(Duration timeout, Executable executable)
metódus a rendelkezésre álló időkeret betartását vizsgálja. Ha a végrehajtásra előírt időkeret lejár és a metódus még nem hajtódott végre, akkor assertál. - A
fail()
metódus feltétel nélkül buktatja a tesztet. - +vannak olyan verziók, ahol a metódus első paramétere egy hibaüzenet
Tesztesetek definiálása¶
- A tesztosztály, tesztesetek publikusan legyenek definiálva
- A metódusokat annotációkkal látjuk el
@Test
- a metódus egy teszteset (test case)
- opcionális paraméterei:
expected
,timeout
- ha lefut az így annotált metódus, akkor a teszteset sikeresnek számít (nincs assert, nincs kivételdobás)
@ParameterizedTest
- Junit5-ben inkább használjuk ezt az annotációt, amennyiben paraméterezett tesztet futtatunk
@ValueSource
annotációval párban
Junit példa:
1 2 3 4 5 6 7 8 9 10 |
|
@RepeatedTest
- A Junit5 lehetőséget ad a teszt ismételt futtatására.
@DisplayName
- Egyedi név deklarálása, amely a futtatókörnyezetben és a reportban is megjelenik.
@BeforeEach/AfterEach
(Junit4-ben:@Before/@After
)- Az ilyen annotációval ellátott metódus lefut MINDEN teszteset hívása előtt/után.
@BeforeAll/AfterAll
(Junit4-ben:@BeforeClass/@AfterClass
)- A metódus lefut EGYSZER a tesztelés kezdetén/végén.
@Tag
- Lehetőség van címkéket rendelni a tesztekhez, amelyek később szűrésre is használhatók.
@Disabled
(Junit4-ben:@Ignore
)- Csak
@Test
-tel párban használható: az így megjelölt tesztesetet a keretrendszer nem futtatja le. Akkor használják, ha a tesztesetet már definiálták, de az adott egység implementációja még nem teljes.
Tesztek futtatása¶
- Statikusan:
run()
metódusokkal saját kódból - Dinamikusan:
TestRunner
osztályok segítségével - Konzolos TestRunner:
java junit.textui.TestRunner
- Eclipse integráció:
- Jobb klikk a tesztosztályon:
Run As | JUnit Test
- Legtöbb Eclipse verzióban már alapból benne van a JUnit plugin
Példa probléma¶
Hajókra tervezünk ütközéselkerülő rendszert. A rendszer alapja, hogy a saját hajó navigációs radarja érzékeli a másik hajó relatív helyzetét és relatív haladási irányát. A radarképeket polárkoordinátás rendszerben kapjuk meg.
Polárkoordináták
A polárkoordináta-rendszer olyan kétdimenziós koordináta-rendszer, ahol a sík minden pontját egy szög és egy távolságadattal adjuk meg. A távolság az origótól mért távolság, amit néha sugárnak is neveznek, míg a szöget irányszögnek nevezik, amelyet úgy határozunk meg, hogy az origóból induló félegyenes és az x tengely pozitív fele által bezárt szöget nézzük. A pozitív irány az óramutató járásával ellentétes irány.
Radarképre példa alábbi ábra:
A hajó helyzetét egy x,y
koordinátapárban adjuk meg, ahol az y
tengely a hajó haladási irányával párhuzamos (a haladási irány felé pozitív), az x
tengely pedig arra merőleges (haladási iránytól jobbra pozitív), és a koordináta rendszer origójában van a saját hajó.
A másik hajó relatív haladási irányát fokban kapjuk meg (ez egy 0-359 közötti szám: 0 ha párhuzamosan azonos irányba; 90 ha merőlegesen balról jobbra; 180 ha párhuzamosan de ellentétes irányba; 270, ha merőlegesen jobbról balra halad). A fenti a relatív haladási irányt jelző szöget a sárgával jelölt terület mutatja.
Nem az irányszöget adja meg a feladat!
Figyeljünk arra, hogy a feladat nem a másik hajó irányszögét adja meg, hanem egy relatív szögértéket, amely a mi hajónk haladási irányához viszonyít és nem az x tengelyhez.
Ezekből az adatokból a rendszer kiszámolja a két hajó útvonalának metszéspontját. Az ábrán az egyenes és az y tengely metszéspontja a keresett pont.
A rendszer a saját hajó haladási sebességből és hosszából kiszámolja, hogy mikor (milyen időintervallumban) fog ezen a ponton áthaladni előtte-utána 3-3 hajóhossz ráhagyással. Ez azt jelenti, hogy a saját hajó eleje mikor közelíti meg 3 hajóhossznyira a metszéspontot, és a tatja mikor hagyja el legalább 3 hajóhosszal (viszonyítási pontnak a saját hajó geometriai közepét tekintve). Közben érzékeli a másik hajó folyamatosan sugárzott sebesség és hossz adatait, és azokkal is elvégzi a számolást. Ha a két hajó eltérő időintervallumban tartozkodik az adott helyen (az ábrán narancssárgával satírozott danger zónában), akkor nincs baj.
Ha viszont a két időtartamnak van közös része, azaz van olyan időszak, mikor mindkét hajó a danger zónában tartózkodna, akkor több lehetőség adódik:
- Ha a hajók haladási iránya legfeljebb 90 fokban tér el (azaz legfeljebb merőlegesen haladnak, de kicsit sem "egymással szemben"), akkor "a jobbról érkezőnek elsőbbsége van" alapon sebességcsökkentést javasol a rendszer, vagy csak fokozott óvatosságra figyelmeztet.
- Ha viszont a két hajó kicsit is szemben halad egymással, akkor "a könnyebb hajó könnyebben menőverez" alapon javasol elkerülő manővert vagy fokozott óvatosságot. (A tömeg adatokat szintén folyamatosan sugározzák a hajók.)
A probléma, hogy az adatsugárzó rendszerek nem teljesen standardizáltak, ezért a sebesség, hossz és tömeg adatokat különféle hajók különféle mértékegységekben sugározhatják.
- Hosszmérték: cm, m (100cm), km (1000m), in (2.54cm), ft (12in), yd (3ft), mile (1760yd), nm (1852m)
- Tömeg: g, kg (1000g), t (1000kg), oz (28.34952g), lb (16oz)
- Idő: s, m (60s), h (60m)
- Sebesség: m/s, km/h, mi/h, knot (1 nm/h)
Egységtesztek¶
- Töltsd le az Eclipse projektet és csomagold ki
- Töltsd be Eclipse-be
File -> Open Projects from file system
File -> Import ... -> General -> Existing project into workspace
- Ship projektre jobb gombbal kattintás ->
New -> Source folder -> Folder name: test
test
folderre jobb katt ->New -> Package -> Name: scws
- Package-re jobb katt ->
New -> JUnit test case -> New JUpiter test
bepipálva Name
->UnitTests.java
Which method subs would you like to create?
- Mind a négyet ki kell pipálni.- Egészítsük ki a projektet az alábbi tesztekkel:
Helyes Unit teszt (a Time
osztály as
függvényét teszteli.)
1 2 3 4 5 6 |
|
Helytelen (bukó) unit teszt (a Time
osztály as
függvényét teszteli.)
1 2 3 4 5 6 |
|
- Ship projektre jobb katt ->
Run As -> JUnit Test
- Láthatjuk, hogy egy passed és egy failed tesztünk van.
Feladat: egységtesztek
Készíts egységteszteket JUnit segítségével.
Mi a teszt futtatásának eredménye?
Sikerült-e a tesztek segítségével valamilyen hibát felfedezni?
Egységteszt hiba
Az AbstractUnit
osztály int compareTo(T)
metódusa hibás
Integrációs tesztek¶
- Package-re jobb katt ->
New -> JUnit test case -> New JUpiter
test bepipálva Name
->IntegrationTests.java
Which method subs would you like to create?
- Mind a négyet ki kell pipálni.- Egészítsük ki a teszteket az alábbiak szerint, majd futtassuk le az előbb leírt módon
Helyes Integration test (hossz, idő és sebesség integrációja)
1 2 3 4 5 6 7 |
|
Feladat: integrációs tesztek
Készíts integrációs teszteket JUnit segítségével.
Mi a teszt futtatásának eredménye?
Sikerült-e a tesztek segítségével valamilyen hibát felfedezni?
Integrációs teszt hiba
- A
Length
osztály távolságból és sebességből időt számoló metódusa rosszul működik. - A
ShipCollisionWarningSystem
osztály rosszul számolja meg ez "előtt" és "mögött" fogalmát.
Rendszertesztek¶
Bár a JUnit (és a hasonló keretrendszerek) alapvetően egységtesztek megírására lettek tervezve, ez nem jelenti azt, hogy nem lehet magasabb szintű teszteket automatizálni a segítségükkel.
Az algoritmus
- Saját hajó (sebesség (V), hossz (L), tömeg (M), relatív irány 0 fok, relatív pozíció (0,0))
-
Másik hajó (sebesség (v), hossz (l), tömeg (m), irány (rd), relatív pozíció (dx,dy)) - ez lényegében a mi koordinátarendszerünkben mért pozíció.
-
Másik hajó irány és pozíció adataiból számoljuk ki, hogy, hogy keresztezi-e a saját hajó útvonalát!
- Ha a másik hajó a jobb oldalon (dx > 0) van és jobbra tart (0° ≤ rd ≤ 180°), vagy a bal oldalon (dx < 0) van és balra megy (180° ≤ rd ≤ 360°(=0°)), akkor nincs keresztezés és figyelmeztetés sem.
- Számoljuk ki az útvonalak metszéspontját!
-
A másik hajó v sebességét vx (nekünk merőleges) és vy (velünk párhuzamos) komponensekre kell felbontani:
Az ábrán is látható, hogy a phi szögből kell kiindulni, a vektor első koordinátája a vektor hossza szorozva az x tengellyel közbezárt szög koszinuszával. Azonban a radar nem az x, hanem az y tengellyel bezárt szöget adja, ami az ábrán az rd. Ezért a phi= 90°-rd alapján:
- vx = v * cos(90° - rd)
- vy = v * sin(90° - rd)
Ha a másik hajó iránya kicsit más, felénk közeledik, akkor a kövezkező ábrát tekintsük:
Itt az a trükk, hogy a phi előjele negatív! Ezért a keresett szöget ugyanúgy kapjuk meg, mint az előző esetben phi= 90°-rd.
- vx = v * cos(90° - rd)
- vy = v * sin(90° - rd)
Minden helyzetben a sebességkomponensek így számolódnak ebben a speciális koordinátarendszerben.
- vx-ből és dx-ből kiszámolható, hogy a másik hajó t = - (dx / vx) (dx negatív előjelű, mert tőlünk balra helyezkedik el - ha meg nem, akkor nem is találkozunk vele) idő múlva keresztezi a saját hajó útvonalát
- vy-ból, dy-ból és t-ből kiszámolható, hogy a másik hajó az Y = dy + vy * t helyen keresztezi a saját hajó útvonalát (X=0, mivel mi definíció szerint az y tengelyen haladunk)
-
Mikor ér a két hajó az útvonalak kereszteződéséhez?
- A másik hajó az előbb kiszámolt t idő múlva
- Mi T = Y / V idő múlva (V a sebességünk)
- Ütközünk?
- A másik hajó dt = 3.5 * l / v idővel a keresztezés előtt lép be, és ennyivel ez után lép ki a danger zónából, vagyis [t-dt, t+dt] időintervallumban tartózkodik ott. (3 hajóhossz + fél hajóhossz, mert a hajó orrától és tatjától számoljuk a 3 hossz ráhagyást).
- Hasonlóan, a saját hajó esetén ez dT = 3.5 * L / V, és [T-dT, T+dT] a keresett időintervallum.
- Ha a két időintervallum nem metszi egymást, akkor nincs figyelmeztetés.
- Melyik hajó tér ki?
- Ha 270° ≤ rd ≤ 360° vagy 0° ≤ rd ≤ 90°, akkor
- Ha dx > 0, a másik hajó mehet, a saját hajó lassít.
- Ha dx < 0, a sajét hajó mehet (óvatosan), a másik hajó lassít.
- Ha 90° < rd < 270°, akkor
- Ha m > M, akkor a másik hajó mehet (óvatosan), és a saját hajónak kell manővereznie.
- Ha M > m, akkor a saját hajó mehet (óvatosan), és a másik hajónak kell manővereznie.
Házi feladat: JUnit használata magasabb szintű tesztekhez
Készíts rendszerteszteket JUnit segítségével. A tesztesetek legyenek a fenti leírás szerintiek. A feladat beadási határideje a következő órát megelőző nap 8.00 óra. A feladatot a Coospace felületen kell beadni. A formátuma egy tömörített (zip) állomány, amely minimálisan tartalmazza a teszt forráskódját és a futási eredményt (erről készített dokumentumot, vagy képernyőképet). Javasoljuk a teljes projekt beadását (azaz az eredeti kódot és a teszteseteket együtt csomagoljuk be a projektállományokkal egyetemben), ez az értékelést is megkönnyíti.
Mi a teszt futtatásának eredménye?
Sikerült-e a tesztek segítségével valamilyen hibát felfedezni?
Rendszerteszt hiba
Ha pont szemből jön a másik hajó, akkor lehet, hogy biztonságos oldaltávolság ellenére is figyelmeztetést kapunk.