Kihagyás

Unit tesztelés a gyakorlatban

Bevezető

Szoftver tesztelés során több szintű tesztelésről is beszélhetünk. Nézzük meg pl. a V-model fejlesztési modelt.

kep

A model alján a kódolás felett található a Unit testing. Ezen tesztelés során a program legkisebb elemét, azaz moduljait teszteljük.

Unit tesztelés

Általában a fejlesztők írják ezeket a teszteket a saját implementált kódjukra. Tehát minden egyes legkisebb egység megírása után írhatunk rá egy unit tesztet. A legkisebb egység, azaz unit, a legtöbbször metódust jelent. Tehát a metódus működését fogjuk letesztelni. Pozitív irányba mindenképpen kell teszteket írnunk, de negatív irányban is érdemes.

Warning

Ezen a tesztelési szinten csak a legkisebb egységhez tartozó adattagokat és működést használjuk és teszteljük. Pl. ha webshopon regisztrálást akarunk tesztelni, akkor adatbázis kapcsolatot kell nyitnunk. Ezesetben az adatbázisból kinyerhető információt mockolnunk kell és a hard codeolt adatra kell letesztelnünk a metódusunkat.

Unit tesztelési technikák

  • Tesztelési folyamat történhet automatikusan vagy kézzel
  • Kézi tesztelés esetén a unit teszteket egy általunk írt tesztprogramból futtatjuk
  • Automatikus tesztelés esetén a szükséges unit tesztet egy tesztelő keretrendszer (framework) futtatja különböző tesztesetekre, izolálva a unitot eredeti környezetétől
  • Az elvárt eredmények kritériumait a programozónak kell definiálni

Előnyök

  • Programhelyesség
  • Korai szakaszban kiszűr sok hibát
  • Helyes programozási szemléletre motivál (alacsony csatolás, magas kohézió)
  • Gyakran szoros kapcsolatban áll a tervezési mintákkal és a refactoringgal, ezáltal a specifikációhoz legközelebb álló megoldást kaphatjuk (jó tesztesetek megválasztása)

Tippek

  • Egy metódust minden lehetséges lényeges paraméterrel teszteljünk, ne csak jó paraméterekkel, próbáljuk meg felborítani a tesztelt osztály működését
  • Hibajavítás, új funkcionalitás után nézzük meg, hogy még mindig megy-e a unit teszt, esetleg kell-e módosítani rajta
  • A tesztosztályokat különítsük el fizikailag a kódfájloktól, de tegyük azonos csomagba/névtérbe
  • A tesztosztályok neve legyen azonos a tesztelt osztály nevével, „Test” végződéssel ellátva
  • Mindig az interfészen keresztül teszteljük a tényleges osztályt, amennyiben megvalósít valamilyen interfészt
  • Egy tesztosztály egy kódosztályt teszteljen
  • Felesleges unit tesztelni: getter/setterek, külső libek

JUnit

  • http://www.junit.org (freeware)
  • Az xUnit család tagja, Java implementáció
  • Reflection-t használ a tesztek futtatásához
  • Könnyen, gyorsan lehet új teszteket készíteni
  • Tesztcsoportokat készíthetünk (suite)
  • Több módon futtatható, azonnali eredmény
  • Automatizálható, integrálható fejlesztőeszközök alá
  • Nem minden esetben használható
  • GUI tesztelés (korlátozottan, nem kényelmes)
  • Web alkalmazások, elosztott rendszerek, SOA…

Assert metódusok

  • Tesztelés közben ellenőrizzük, hogy a megfelelő eredményeket kapjuk-e – Assert metódusokkal
  • Ha nem megfelelő a kapott eredmény, a teszteset sikertelennek minősül (failed)
  • Statikus metódusok (Java 5.0 – static import)
  • assertFalse(condition), assertTrue(condition)
  • assertEquals(expected, actual)
  • assertArrayEquals(expected[], actual[])
  • assertNull(object), assertNotNull(object)
  • assertSame(expected, actual), assertNotSame(…)
  • fail()
  • +vannak olyan verziók, ahol az első paraméter egy hibaüzenet

Tesztek, tesztesetek definiálása

  • A tesztosztály, tesztesetek publikusan legyenek
  • A metódusokat annotációkkal látjuk el
  • @Test
  • a metódus egy teszteset (test case)
  • opcionális paraméterek: expected, timeout
  • ha lefut, a teszteset sikeresnek számít (nincs assert, exception)
  • @Before / @After
  • ez a metódus lefut MINDEN teszteset hívása előtt/után
  • @BeforeClass / @AfterClass
  • a metódus lefut EGYSZER a tesztelés kezdetén/végén
  • @Ignore
  • csak @Test-tel párban: ezt a tesztesetet nem futtatja le

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

JUnit 5 használata a gyakorlatban

Példa projekt megismerése

Grade (Histogram) Számoljuk össze a névsor és a kapott pontok alapján, hogy hányan teljesítették a tárgyat elégségesen, kiemelkedően, stb.

Névsor és pontok:

Pista 1
Peti 100
Laci 90
Zoli 50
Gergő 10
András 12

Megoldása

  • Létrehozunk egy hisztogramot.
  • Belerakunk egy-két vödröt.
  • Feltöltjük elemekkel.
  • Kiíratjuk a vödrök nagyságát Tehát, az első vödör 2 elemű lesz: Pista, Gergő. A második 1 elemű: Zoli. A harmadik pedig 2 elemű: Peti és Laci.

Ezt megírjuk, lefuttatjuk. Látjuk, hogy ezekre működik, de minden egyes inputra jól működik a program? Mi történik ha valaki 100 pontnál többet kap, vagy éppen 0 pontnál kevesebbet?

Teszteljük le a programot

Tesztelés célja

A tesztelés elsődleges célja hibás viselkedés kimutatása. A letesztelt program nem tökéletes. Ha nem találtál hibát akkor a tesztelés sikertelen.

Hogyan írjunk unit tesztet?

Gyakorlati tippek

  • Kipróbálás alapján Main.main(String[ ] args)
  • Beégetett értékekkel teszteljünk
  • "alma", 123, True
  • r . nextInt (201), sort (5, 3, 4, 2), LocalDateTime.now()
  • Kivételes viselkedést is teszteljük (hibás, nem várt input. . . ) assertThrows( exceptionClass , act)
  • Azt is igazoljuk, hogy a program nem csinálja azt, amit nem kéne histogram.addBucket(new Limit(0, 15)); histogram.addBucket(new Limit(0, 15));
  • Teszteseteket meg kell tudni ismételni, hibás esetben is „beírtam valamit és nem működött ez az izé” – egy névtelen felhasználó

Készítsünk unit tesztet

Feladat

Alakítsuk át a Main.main( ) metódust unit tesztté

Kód átemelése unit teszt metódusba:

Másoljuk át a kódot egy unit teszt fájlba. Minden egyes teszt metódus elé a @Test annotációt ki kell tennünk

Nem-determinisztikus hívások kicserélése beégetett értékekre:

Tehát random értékek helyett konkrétakat írjunk. Ezt azért érdemes (és muszáj) megtennünk, mivel ha találunk hibát annak reprodukálhatónak kell lennie.

Kiíratás helyettesítése megfelelő assert metódussal

A tesztelő keretrendszer (pl. JUnit) ezzel ellenőrzi, hogy jó-e a metódus

Megoldás

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@Test void testFill(){
  Histogramhistogram=newHistogram();
  histogram.addBucket(newLimit(0,15));
  histogram.addBucket(newLimit(16,30));
  histogram.addBucket(newLimit(50,150));
  Map<String,Integer>scores=newHashMap<>();
  scores.put("Moomintroll",0);
  scores.put("Moominpappa",20);
  scores.put("Moominmamma",10);
  histogram.fill(scores);
  assertEquals(2,histogram.getBuckets().get(0).size());
  assertEquals(1,histogram.getBuckets().get(1).size());
  assertEquals(0,histogram.getBuckets().get(2).size());
}

Unit tesztek szerkezete

Arrange

  • Tesztelendő környezet előkészítése
  • Objektum példányok létrehozása
  • Adattagok beállítása
  • @Before @After annotációk használatosak a fenti háromhoz, melyekkel megmondhatjuk, hogy a teszt metódusok előtt és után mik történjenek, ha minden teszt előkészítése azonos!
  • Bemeneti paraméterek definiálása

Act

  • Tesztelendő rész végrehajtása
  • Tesztelendő metódus hívása
  • Viselkedés és visszatérési értékek rögzítése

Assert

  • Teszt kiértékelése
  • Kapott és elvárt eredmények összehasonlítása
  • A unit tesztelés egyik legfontosabb része, ugyanis ezzel ellenőrizzük le, hogy jó-e a kódunk.
  • Számos assert létezik: ellenőrizhetünk stringeket, booleaneket, inteket, listákat, stb. Pl.: assertEquals(double expected, double actual) assertNull(java.lang.Object object) assertTrue(boolean condition)
  • Link az assertek fajtáira.
  • A tetszőlegesen választott IDE fel fogja ajánlani az assertek listáját ha elkezdjük begépelni azt, hogy assert.

Elvárt és kapott eredmények, viselkedések

Explicit jelezzük a szándékunk

1
2
3
var input = ... ; //optional
var actual= ... ;
var expected = ... ;

Programozó vagy tesztelő?

  • alacsony szintű követelmények jelzése: assert expression ; pl.: egy változó nem lehet üres string
  • teszt eredmények ellenőrzése: org. junit . jupiter . api . Assertions

Feladat

  • Alakítsuk át a korábbi tesztet az AAA mintának megfelelően.
  • kommentezéssel jelöljük az egyes részeket
  • alkalmazzunk megfelelő változó neveket

Elvárt hiba

  • Kivételes viselkedés tesztelése

    //arrange histogram.addBucket(new Limit(0 , 100 )) ; //act & assert assertThrows(IncompatibleBucketException.class , ( ) −> histogram.addBucket(new Limit(−10 , 50 ) ) ) ;

Helytelen bemenet

1
2
//act 
histogram.addBucket(new Limit(null , null ) )

Feladat A testAddBucket_invalidLimit() alapján javítsuk ki a converter.Limit oszály konstruktorát.

Egy metódus – Több teszt

Input „osztályonkénti” tesztelés

1
2
3
 //arrange
  histogram.addBucket(new Limit( 0 , 100 ) ) ; 
  //act
alsó metszés histogram.addBucket(new Limit(−10, 50))
felső metszés histogram.addBucket(new Limit(50, 110))
mindkettő metszés histogram.addBucket(new Limit(−10, 110))
  • Dummy, stub és mock

További unit tesztelés tippek

  • Egy metódust minden lehetséges lényeges paraméterrel teszteljünk, ne csak jó paraméterekkel, próbáljuk meg felborítani a tesztelt osztály működését
  • Például, ha valakinek a születési évét kéri a függvény, akkor ellenőrizzük le, hogy 1900 alatt mit ad vissza. Ugyanígy leellenőrizhetjük nem egész számokra is.
  • Hibajavítás, új funkcionalitás után nézzük meg, hogy még mindig megy-e a unit teszt, esetleg kell-e módosítani rajta
  • Futtassuk le újra Run As JUnit gombbal. Ha mindegyik zöld, akkor örülünk. Ha valamelyik piros akkor mégjobban. Ugyanis a piros jelzi azt, hogy találtunk hibát a programban, amit tudunk javítani, mielőtt a felhasználóhoz került volna.
  • A tesztosztályokat különítsük el fizikailag a kódfájloktól, de tegyük azonos csomagba/névtérbe
  • Tehát csináljunk egy „test” mappát és ide rakjuk a teszt fájlokat azaz a unit tesztjeinket.
  • A tesztosztályok neve legyen azonos a tesztelt osztály nevével, „Test” végződéssel ellátva
  • Például ha a tesztelni kívánt metódus neve az, hogy „saveFile” akkor a teszt metódusunk neve legyen: „saveFileTest”
  • Mindig az interfészen keresztül teszteljük a tényleges osztályt, amennyiben megvalósít valamilyen interfészt
  • Egy tesztosztály egy kódosztályt teszteljen
  • Felesleges unit tesztelni: triviális megvalósításokat, külső libek Getter setterekben ha nincsen különösebb logika, akkor fölösleges letesztelni.
  • Külső libek nem unitok, ezért nincs értelme unit tesztelni, mert azokat a lib fejlesztői már letesztelték.
  • Ha egy metódus egy külső lib függvényét használja, akkor mockoljuk.
  • Mockolás: megadjuk, hogy bizonyos helyzetekben hogy viselkedjen a meghívandó külső függvény.

Utolsó frissítés: 2021-03-30 06:02:13