Kihagyás

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:

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éterei Iterable 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 az expected és actual 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
    import org.junit.jupiter.params.ParameterizedTest;
    import org.junit.jupiter.params.provider.ValueSource;
    import static org.junit.jupiter.api.Assertions.assertTrue;
    class JUnit5Test {
        @ParameterizedTest
        @ValueSource(strings = { "cali", "bali", "dani" })
    void endsWithI(String str) {
          assertTrue(str.endsWith("i"));
     }
    }
  • @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:

Radar

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ó.

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
@Test
    void testAsRelations() throws InvalidUnitException {
    assertEquals(1.0, new Time(   1.0, "s").as("s"));
    assertEquals(1.0, new Time(  60.0, "s").as("m"));
    assertEquals(1.0, new Time(3600.0, "s").as("h"));
}

Helytelen (bukó) unit teszt (a Time osztály as függvényét teszteli.)

1
2
3
4
5
6
@Test
void testAsRelationsBadExample() throws InvalidUnitException {
    assertEquals(1.0, new Time(   1.0, "s").as("s"));
    assertEquals(1.0, new Time(  59.9, "s").as("m"));
    assertEquals(1.0, new Time(3600.0, "s").as("h"));
}
  • 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
@Test
void testLengthPerTime() throws InvalidUnitException {
    assertEquals(new Speed(1.00, "m/s"), new Length(1.0, "m").div(new Time(1.0, "s")));
    assertEquals(new Speed(1.00, "km/h"), new Length(1.0, "km").div(new Time(1.0, "h")));
    assertEquals(new Speed(1.00, "mi/h"), new Length(1.0, "mi").div(new Time(1.0, "h")));
    assertEquals(new Speed(1.00, "knot"), new Length(1.0, "nm").div(new Time(1.0, "h")));
}

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:

    Sebesség 1

    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:

    Sebesség 2

    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.


Utolsó frissítés: 2024-09-02 12:56:34